ostensible read pattern:
Taken from here.
Here is what I observe:
above logic analyzer signals are in the same order as the official ftdi screenshot. Looks like rxf# is going low rather slowly.
..This turned out not to be the issue. the issue was just that I wasn’t putting the ft232h into the right sync fifo mode.
Working, effect of coax loading
Here is the r2r dac working properly finally. I have a ramp, the falling edge of which can be seen here:
This is with 1kR resistors. So to meet the 10MHz spec they would need to be 1/2 of what they are to go from 200ns fall time to 100ns.
State machine tribulations
The logic bus is defined as follows:
always @(posedge clk100) begin
logic_out[2:0] <= state_dbg;
logic_out[3] <= data_cnt_dbg[0];
logic_out[4] <= awg_valid;
logic_out[5] <= fsm_tvalid;
logic_out[6] <= fsm_tready;
logic_out[7] <= fsm_tdata[7];
end
Here is the state machine after the first packet sent from the PC to the AWG:
And a bunch of packets after that: (~10)
The definitions of the state machine are here:
// State machine parameters
localparam [2:0] S_IDLE = 3'b010,
S_SET_LENGTH = 3'b001,
S_TRANSFER = 3'b011,
S_COOLDOWN = 3'b101;
reg [2:0] state = S_IDLE;
assign state_dbg = state;
So you can see it starts off in S_IDLE
which is correct, but ends up in S_TRANSFER
, which is wrong, it should be back to S_IDLE
. Here is the transfer that happened just before the bad one:
This one has 9 pulses on the fsm_tready line not the normal 8!
Next day
I sent out some PCB’s which will hopefully allow me to use the full 16 bits of the digital inputs of my scope rather than the current manually-soldered-two-ethernet-cables 8.
Next couple of days
I went and discussed the above protocol where [header byte], [length], [waveform data] was sent and apparently this is a bad way to do things because if you pop a cog somewhere you start interpreting waveform data as a header/length, and then you are completely lost. I agree with this take. Instead, you should reserve a bit or a magic value in the stream such that the state of the fpga can be reset at any time. Here is a diagram claude made for me to describe the reserved bit version of the system:
I implemented this and indeed it seems at first glance to be much better. Here is a sin wave sent out:
The horrible nonlinearities in the sin wave are just the R2R DAC. you can see from the digital stream that it’s working! So that’s nice. The stream does have a tendency to lock up sometimes though, so I need to figure that out.
Bit untwiddling
It also has this bug, whereby when I send a message that’s large enough that the ftdi chip needs to do flow control, I get something that looks like this sometimes:
Which looks like a bit got flipped somewhere. That is one problem though. The more eggregious one is where the whole state machine locks up somehow and doesn’t let any more data in:
The data definitions are as follows:
always @(ftdi_clk) begin
logic_out[0] <= ftdi_clk;
logic_out[1] <= ftdi_rxf_n;
logic_out[2] <= ftdi_oe_n;
logic_out[3] <= ftdi_fsm_ready;
logic_out[4] <= ftdi_fsm_valid;
logic_out[5] <= awg_valid;
logic_out[6] <= ftdi_fsm_data[0];
logic_out[7] <= ftdi_data[0];
end
From the ftdi datasheet about the way that data flows through the ftdi chip for a read operation:
So we can see from bits 1 and 2 that the ftdi is signalling via rxf that there is data available, and from bit 2 oe that we are not requesting any data. That goes into the third party fifo and then data from that fifo is signalled as being ready by ftdi_fsm_ready
(because the data is flowing from the ftdi chip into my finite state machine).
That in turn says there is data available. But ftdi_fsm_valid
goes low for a good long while during which the awg output is flat. It goes high again after that and some stuff happens but I think that’s the chook running around with its head off at that point.
Here is the entirety of the “state machine”:
module state_machine (
input wire clk100,
input wire in_tvalid,
output reg in_tready = 1,
input wire [7:0] in_tdata,
output reg awg_valid = 0,
input wire awg_ready,
output reg [7:0] awg_out,
output reg [6:0] trigger_mode,
// Debugging:
output wire [2:0] state_dbg,
output wire [31:0] data_cnt_dbg
);
assign data_cnt_dbg[7:0] = in_tdata;
wire cmd_bit = in_tdata[7]; // Debugging
wire [6:0] wire_payload = in_tdata[6:0]; // Debugging
reg [7:0] new_bits_in_sample = 0;
reg [7:0] awg_partial_lhs = 0;
reg [7:0] awg_partial_rhs = 0;
reg [7:0] awg_partial = 0;
reg [7:0] bits_in_sample = 0;
reg [7:0] awg_next = 0;
// reg awg_valid = 0;
reg [8*22-1:0] state_name; // Debugging
// wire [7:0] bits_left_in_sample = 8 - bits_in_sample;
always @(posedge clk100) begin
if (awg_ready && awg_valid) begin
awg_out <= awg_next;
awg_valid <= 0;
in_tready <= 1;
end
if (in_tvalid) begin
if (in_tdata[7] == 1) begin // Command
if (wire_payload == `TRIGGER_MODE_NONE || wire_payload == `TRIGGER_MODE_EDGE) begin
trigger_mode <= wire_payload;
state_name <= "CMD_TRIG";
end else if (wire_payload == `RESET_TRANSMISSION) begin
state_name <= "CMD_RST";
bits_in_sample <= 0;
awg_partial <= 0;
// We do not invalidate awg_next here as that command has already been issued,
// so that would represent a command reaching back in time.
end else begin
state_name <= "CMD_ERR";
$display("Unknown trigger mode: %b", wire_payload);
end
in_tready <= ~awg_valid;
end else begin
if (bits_in_sample + 7 >= 8) begin
if (in_tready) begin
state_name <= "REMAINDER_ASSIGN";
awg_next <= awg_partial | (in_tdata >> (bits_in_sample - 1));
new_bits_in_sample = (bits_in_sample + 7) % 8;
awg_partial_lhs = (awg_partial << bits_in_sample) & (8'hFF << new_bits_in_sample);
awg_partial_rhs = (in_tdata << (8 - new_bits_in_sample));
awg_partial <= awg_partial_lhs | awg_partial_rhs;
bits_in_sample <= new_bits_in_sample;
awg_valid <= 1;
in_tready <= 0;
end else begin
state_name <= "REMAINDER_WAiT";
end
end else begin
if (in_tready) begin
state_name <= "NO_REMAINDER";
awg_partial <= in_tdata << 1;
bits_in_sample <= bits_in_sample + 7;
in_tready <= ~awg_valid;
end else begin
state_name <= "~NO_REMAINDER";
in_tready <= 1;
end
// TODO: sim assert that bits in sample is 0 here.
end
end
end else begin
state_name <= "IN_INVALID";
end
end
endmodule