aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp2/serdes
diff options
context:
space:
mode:
authorJosh Blum <josh@joshknows.com>2010-04-15 11:24:24 -0700
committerJosh Blum <josh@joshknows.com>2010-04-15 11:24:24 -0700
commit05d77f772317de5d925301aa11bb9a880656dd05 (patch)
tree0910bfb9265fab1644a3d3a1706719f1b038d193 /fpga/usrp2/serdes
parent16818dc98e97b69a028c47e66ebfb16e32565533 (diff)
downloaduhd-05d77f772317de5d925301aa11bb9a880656dd05.tar.gz
uhd-05d77f772317de5d925301aa11bb9a880656dd05.tar.bz2
uhd-05d77f772317de5d925301aa11bb9a880656dd05.zip
moved usrp1 and usrp2 fpga dirs into fpga subdirectory
Diffstat (limited to 'fpga/usrp2/serdes')
-rw-r--r--fpga/usrp2/serdes/serdes.v63
-rw-r--r--fpga/usrp2/serdes/serdes_fc_rx.v62
-rw-r--r--fpga/usrp2/serdes/serdes_fc_tx.v24
-rw-r--r--fpga/usrp2/serdes/serdes_rx.v292
-rw-r--r--fpga/usrp2/serdes/serdes_tb.v328
-rw-r--r--fpga/usrp2/serdes/serdes_tx.v186
6 files changed, 955 insertions, 0 deletions
diff --git a/fpga/usrp2/serdes/serdes.v b/fpga/usrp2/serdes/serdes.v
new file mode 100644
index 000000000..17049bfe6
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes.v
@@ -0,0 +1,63 @@
+
+// SERDES TX and RX along with all flow control logic
+
+module serdes
+ #(parameter TXFIFOSIZE = 9,
+ parameter RXFIFOSIZE = 9)
+ (input clk, input rst,
+ // TX side
+ output ser_tx_clk, output [15:0] ser_t, output ser_tklsb, output ser_tkmsb,
+ input [31:0] rd_dat_i, input [3:0] rd_flags_i, output rd_ready_o, input rd_ready_i,
+ // RX side
+ input ser_rx_clk, input [15:0] ser_r, input ser_rklsb, input ser_rkmsb,
+ output [31:0] wr_dat_o, output [3:0] wr_flags_o, output wr_ready_o, input wr_ready_i,
+
+ output [15:0] tx_occupied, output tx_full, output tx_empty,
+ output [15:0] rx_occupied, output rx_full, output rx_empty,
+
+ output serdes_link_up,
+
+ output [31:0] debug0,
+ output [31:0] debug1);
+
+ wire [15:0] fifo_space;
+ wire xon_rcvd, xoff_rcvd, inhibit_tx, send_xon, send_xoff, sent;
+ wire [31:0] debug_rx, debug_tx;
+
+ serdes_tx #(.FIFOSIZE(TXFIFOSIZE)) serdes_tx
+ (.clk(clk),.rst(rst),
+ .ser_tx_clk(ser_tx_clk),.ser_t(ser_t),.ser_tklsb(ser_tklsb),.ser_tkmsb(ser_tkmsb),
+ .rd_dat_i(rd_dat_i),.rd_flags_i(rd_flags_i),.rd_ready_o(rd_ready_o),.rd_ready_i(rd_ready_i),
+ .inhibit_tx(inhibit_tx), .send_xon(send_xon), .send_xoff(send_xoff), .sent(sent),
+ .fifo_occupied(tx_occupied),.fifo_full(tx_full),.fifo_empty(tx_empty),
+ .debug(debug_tx) );
+
+ serdes_rx #(.FIFOSIZE(RXFIFOSIZE)) serdes_rx
+ (.clk(clk),.rst(rst),
+ .ser_rx_clk(ser_rx_clk),.ser_r(ser_r),.ser_rklsb(ser_rklsb),.ser_rkmsb(ser_rkmsb),
+ .wr_dat_o(wr_dat_o),.wr_flags_o(wr_flags_o),.wr_ready_o(wr_ready_o),.wr_ready_i(wr_ready_i),
+ .fifo_space(fifo_space), .xon_rcvd(xon_rcvd), .xoff_rcvd(xoff_rcvd),
+ .fifo_occupied(rx_occupied),.fifo_full(rx_full),.fifo_empty(rx_empty),
+ .serdes_link_up(serdes_link_up), .debug(debug_rx) );
+
+ serdes_fc_tx serdes_fc_tx
+ (.clk(clk),.rst(rst),
+ .xon_rcvd(xon_rcvd),.xoff_rcvd(xoff_rcvd),.inhibit_tx(inhibit_tx) );
+
+ serdes_fc_rx #(.LWMARK(32),.HWMARK(128)) serdes_fc_rx
+ (.clk(clk),.rst(rst),
+ .fifo_space(fifo_space),.send_xon(send_xon),.send_xoff(send_xoff),.sent(sent) );
+
+ //assign debug = { fifo_space, send_xon, send_xoff, debug_rx[13:0] };
+ //assign debug = debug_rx;
+
+ assign debug0 = { { 2'b00, rd_ready_o, rd_ready_i, rd_flags_i[3:0]},
+ { debug_tx[5:4] /* full,empty */ , inhibit_tx, send_xon, send_xoff, sent, ser_tkmsb, ser_tklsb},
+ { ser_t[15:8] },
+ { ser_t[7:0] } };
+
+ assign debug1 = { { debug_rx[7:0] }, /* odd,xfer_active,sop_i,eop_i,error_i,state[2:0] */
+ { wr_flags_o[1:0], wr_ready_i, wr_ready_o, xon_rcvd, xoff_rcvd, ser_rkmsb, ser_rklsb },
+ { ser_r[15:8] },
+ { ser_r[7:0] } };
+endmodule // serdes
diff --git a/fpga/usrp2/serdes/serdes_fc_rx.v b/fpga/usrp2/serdes/serdes_fc_rx.v
new file mode 100644
index 000000000..4dd46e27f
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes_fc_rx.v
@@ -0,0 +1,62 @@
+
+
+module serdes_fc_rx
+ #(parameter LWMARK = 64,
+ parameter HWMARK = 320)
+ (input clk, input rst,
+ input [15:0] fifo_space,
+ output reg send_xon,
+ output reg send_xoff,
+ input sent);
+
+ reg [15:0] countdown;
+ reg send_xon_int, send_xoff_int;
+
+ always @(posedge clk)
+ if(rst)
+ begin
+ send_xon_int <= 0;
+ send_xoff_int <= 0;
+ countdown <= 0;
+ end
+ else
+ begin
+ send_xon_int <= 0;
+ send_xoff_int <= 0;
+ if(countdown == 0)
+ if(fifo_space < LWMARK)
+ begin
+ send_xoff_int <= 1;
+ countdown <= 240;
+ end
+ else
+ ;
+ else
+ if(fifo_space > HWMARK)
+ begin
+ send_xon_int <= 1;
+ countdown <= 0;
+ end
+ else
+ countdown <= countdown - 1;
+ end // else: !if(rst)
+
+ // If we are between the high and low water marks, we let the countdown expire
+
+ always @(posedge clk)
+ if(rst)
+ send_xon <= 0;
+ else if(send_xon_int)
+ send_xon <= 1;
+ else if(sent)
+ send_xon <= 0;
+
+ always @(posedge clk)
+ if(rst)
+ send_xoff <= 0;
+ else if(send_xoff_int)
+ send_xoff <= 1;
+ else if(sent)
+ send_xoff <= 0;
+
+endmodule // serdes_fc_rx
diff --git a/fpga/usrp2/serdes/serdes_fc_tx.v b/fpga/usrp2/serdes/serdes_fc_tx.v
new file mode 100644
index 000000000..2fe967c8d
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes_fc_tx.v
@@ -0,0 +1,24 @@
+
+
+module serdes_fc_tx
+ (input clk, input rst,
+ input xon_rcvd, input xoff_rcvd, output reg inhibit_tx);
+
+ // XOFF means stop sending, XON means start sending
+ // clock domain stuff happens elsewhere, everything here is on main clk
+
+ reg [15:0] state;
+ always @(posedge clk)
+ if(rst)
+ state <= 0;
+ else if(xoff_rcvd)
+ state <= 255;
+ else if(xon_rcvd)
+ state <= 0;
+ else if(state !=0)
+ state <= state - 1;
+
+ always @(posedge clk)
+ inhibit_tx <= (state != 0);
+
+endmodule // serdes_fc_tx
diff --git a/fpga/usrp2/serdes/serdes_rx.v b/fpga/usrp2/serdes/serdes_rx.v
new file mode 100644
index 000000000..afefccaa1
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes_rx.v
@@ -0,0 +1,292 @@
+
+// SERDES Interface
+
+// LS-Byte is sent first, MS-Byte is second
+// Invalid K Codes
+// K0.0 000-00000 Error detected
+// K31.7 111-11111 Loss of input signal
+
+// Valid K Codes
+// K28.0 000-11100
+// K28.1 001-11100 Alternate COMMA?
+// K28.2 010-11100
+// K28.3 011-11100
+// K28.4 100-11100
+// K28.5 101-11100 Standard COMMA?
+// K28.6 110-11100
+// K28.7 111-11100 Bad COMMA?
+// K23.7 111-10111
+// K27.7 111-11011
+// K29.7 111-11101
+// K30.7 111-11110
+
+module serdes_rx
+ #(parameter FIFOSIZE = 9)
+ (input clk,
+ input rst,
+
+ // RX HW Interface
+ input ser_rx_clk,
+ input [15:0] ser_r,
+ input ser_rklsb,
+ input ser_rkmsb,
+
+ output [31:0] wr_dat_o,
+ output [3:0] wr_flags_o,
+ input wr_ready_i,
+ output wr_ready_o,
+
+ output [15:0] fifo_space,
+ output xon_rcvd, output xoff_rcvd,
+
+ output [15:0] fifo_occupied, output fifo_full, output fifo_empty,
+ output reg serdes_link_up,
+ output [31:0] debug
+ );
+
+ localparam K_COMMA = 8'b101_11100; // 0xBC K28.5
+ localparam K_IDLE = 8'b001_11100; // 0x3C K28.1
+ localparam K_PKT_START = 8'b110_11100; // 0xDC K28.6
+ localparam K_PKT_END = 8'b100_11100; // 0x9C K28.4
+ localparam K_XON = 8'b010_11100; // 0x5C K28.2
+ localparam K_XOFF = 8'b011_11100; // 0x7C K28.3
+ localparam K_LOS = 8'b111_11111; // 0xFF K31.7
+ localparam K_ERROR = 8'b000_00000; // 0x00 K00.0
+ localparam D_56 = 8'b110_00101; // 0xC5 D05.6
+
+ localparam IDLE = 3'd0;
+ localparam FIRSTLINE1 = 3'd1;
+ localparam FIRSTLINE2 = 3'd2;
+ localparam PKT1 = 3'd3;
+ localparam PKT2 = 3'd4;
+ localparam CRC_CHECK = 3'd5;
+ localparam ERROR = 3'd6;
+ localparam DONE = 3'd7;
+
+ wire [17:0] even_data;
+ reg [17:0] odd_data;
+ wire [17:0] chosen_data;
+ reg odd;
+
+ reg [31:0] line_i;
+ reg sop_i, eop_i, error_i;
+ wire error_o, sop_o, eop_o, write;
+ reg [15:0] halfline;
+ reg [8:0] holder;
+ wire [31:0] line_o;
+
+ reg [2:0] state;
+
+ reg [15:0] CRC;
+ wire [15:0] nextCRC;
+ reg write_d;
+
+ wire rst_rxclk;
+ wire have_space;
+
+ oneshot_2clk rst_1s(.clk_in(clk),.in(rst),.clk_out(ser_rx_clk),.out(rst_rxclk));
+
+ assign even_data = {ser_rkmsb,ser_rklsb,ser_r};
+
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk)
+ holder <= 9'd0;
+ else
+ holder <= {even_data[17],even_data[15:8]};
+
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk)
+ odd_data <= 18'd0;
+ else
+ odd_data <= {even_data[16],holder[8],even_data[7:0],holder[7:0]};
+
+ assign chosen_data = odd ? odd_data : even_data;
+
+ // Transfer xon and xoff info to the main system clock for flow control purposes
+ reg xon_rcvd_rxclk, xoff_rcvd_rxclk;
+ always @(posedge ser_rx_clk)
+ xon_rcvd_rxclk = ({1'b1,K_XON} == {ser_rkmsb,ser_r[15:8]}) | ({1'b1,K_XON} == {ser_rklsb,ser_r[7:0]} );
+ always @(posedge ser_rx_clk)
+ xoff_rcvd_rxclk = ({1'b1,K_XOFF} == {ser_rkmsb,ser_r[15:8]}) | ({1'b1,K_XOFF} == {ser_rklsb,ser_r[7:0]} );
+
+ oneshot_2clk xon_1s(.clk_in(ser_rx_clk),.in(xon_rcvd_rxclk),.clk_out(clk),.out(xon_rcvd));
+ oneshot_2clk xoff_1s(.clk_in(ser_rx_clk),.in(xoff_rcvd_rxclk),.clk_out(clk),.out(xoff_rcvd));
+
+ // If the other side is sending xon or xoff, or is flow controlled (b/c we told them to be), don't fill the fifos
+ wire wait_here = ((chosen_data == {2'b10,K_COMMA,D_56})||
+ (chosen_data == {2'b11,K_XON,K_XON})||
+ (chosen_data == {2'b11,K_XOFF,K_XOFF}) );
+
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk) sop_i <= 0;
+ else if(state == FIRSTLINE1) sop_i <= 1;
+ else if(write_d) sop_i <= 0;
+
+ reg write_pre;
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk)
+ begin
+ state <= IDLE;
+ odd <= 0;
+ halfline <= 0;
+ line_i <= 0;
+ eop_i <= 0;
+ error_i <= 0;
+ write_pre <= 0;
+ end
+ else
+ case(state)
+ IDLE :
+ begin
+ error_i <= 0;
+ write_pre <= 0;
+ if(even_data == {2'b11,K_PKT_START,K_PKT_START})
+ begin
+ state <= FIRSTLINE1;
+ odd <= 0;
+ end
+ else if(odd_data == {2'b11,K_PKT_START,K_PKT_START})
+ begin
+ state <= FIRSTLINE1;
+ odd <= 1;
+ end
+ end
+
+ FIRSTLINE1 :
+ if(chosen_data[17:16] == 0)
+ begin
+ halfline <= chosen_data[15:0];
+ state <= FIRSTLINE2;
+ end
+ else if(wait_here)
+ ; // Flow Controlled, so wait here and do nothing
+ else
+ state <= ERROR;
+
+ FIRSTLINE2 :
+ if(chosen_data[17:16] == 0)
+ begin
+ line_i <= {chosen_data[15:0],halfline};
+ if(~have_space) // No space to write to! Should have been avoided by flow control
+ state <= ERROR;
+ else
+ begin
+ state <= PKT1;
+ write_pre <= 1;
+ end
+ end // if (chosen_data[17:16] == 0)
+ else if(wait_here)
+ ; // Flow Controlled, so wait here and do nothing
+ else
+ state <= ERROR;
+
+ PKT1 :
+ begin
+ write_pre <= 0;
+ if(chosen_data[17:16] == 0)
+ begin
+ halfline <= chosen_data[15:0];
+ state <= PKT2;
+ end
+ else if(wait_here)
+ ; // Flow Controlled
+ else if(chosen_data == {2'b11,K_PKT_END,K_PKT_END})
+ state <= CRC_CHECK;
+ else
+ state <= ERROR;
+ end // case: PKT1
+
+ PKT2 :
+ if(chosen_data[17:16] == 0)
+ begin
+ line_i <= {1'b0,1'b0,1'b0,chosen_data[15:0],halfline};
+ if(~have_space) // No space to write to!
+ state <= ERROR;
+ else
+ begin
+ state <= PKT1;
+ write_pre <= 1;
+ end
+ end // if (chosen_data[17:16] == 0)
+ else if(wait_here)
+ ; // Flow Controlled
+ else
+ state <= ERROR;
+
+ CRC_CHECK :
+ if(chosen_data[17:0] == {2'b00,CRC})
+ begin
+ if(~have_space)
+ state <= ERROR;
+ else
+ begin
+ eop_i <= 1;
+ state <= DONE;
+ end
+ end
+ else if(wait_here)
+ ;
+ else
+ state <= ERROR;
+
+ ERROR :
+ begin
+ error_i <= 1;
+ if(have_space)
+ state <= IDLE;
+ end
+ DONE :
+ begin
+ state <= IDLE;
+ eop_i <= 0;
+ end
+
+ endcase // case(state)
+
+
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk)
+ CRC <= 16'hFFFF;
+ else if(state == IDLE)
+ CRC <= 16'hFFFF;
+ else if(chosen_data[17:16] == 2'b00)
+ CRC <= nextCRC;
+
+ CRC16_D16 crc_blk(chosen_data[15:0],CRC,nextCRC);
+
+ always @(posedge ser_rx_clk)
+ if(rst_rxclk) write_d <= 0;
+ else write_d <= write_pre;
+
+ // Internal FIFO, size 9 is 2K, size 10 is 4K Bytes
+ assign write = eop_i | (error_i & have_space) | (write_d & (state != CRC_CHECK));
+
+ fifo_2clock_cascade #(.WIDTH(35),.SIZE(FIFOSIZE)) serdes_rx_fifo
+ (.arst(rst),
+ .wclk(ser_rx_clk),.datain({error_i,sop_i,eop_i,line_i}),
+ .src_rdy_i(write), .dst_rdy_o(have_space), .space(fifo_space),
+ .rclk(clk),.dataout({error_o,sop_o,eop_o,line_o}),
+ .src_rdy_o(wr_ready_o), .dst_rdy_i(wr_ready_i), .occupied(fifo_occupied) );
+
+ assign fifo_full = ~have_space; // Note -- in the wrong clock domain
+ assign fifo_empty = ~wr_ready_o;
+
+ // Internal FIFO to Buffer interface
+ assign wr_dat_o = line_o;
+ assign wr_flags_o = { 2'b00, eop_o | error_o, sop_o | error_o };
+
+ wire slu = ~(({2'b11,K_ERROR,K_ERROR}=={ser_rkmsb,ser_rklsb,ser_r}) ||
+ ({2'b11,K_LOS,K_LOS}=={ser_rkmsb,ser_rklsb,ser_r}));
+
+ reg [3:0] slu_reg;
+
+ always @(posedge clk)
+ if(rst) slu_reg <= 0;
+ else slu_reg <= {slu_reg[2:0],slu};
+
+ always @(posedge clk)
+ serdes_link_up <= &slu_reg[3:1];
+
+ assign debug = { have_space, wr_ready_o, odd, sop_i, eop_i, error_i, state[2:0] };
+
+endmodule // serdes_rx
diff --git a/fpga/usrp2/serdes/serdes_tb.v b/fpga/usrp2/serdes/serdes_tb.v
new file mode 100644
index 000000000..eb8e019fc
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes_tb.v
@@ -0,0 +1,328 @@
+
+// FIXME need to add flow control
+
+module serdes_tb();
+
+ reg clk, rst;
+ wire ser_rx_clk, ser_tx_clk;
+ wire ser_rklsb, ser_rkmsb, ser_tklsb, ser_tkmsb;
+ wire [15:0] ser_r, ser_t;
+
+ initial clk = 0;
+ initial rst = 1;
+ initial #1000 rst = 0;
+ always #100 clk = ~clk;
+
+ // Wishbone
+ reg [31:0] wb_dat_i;
+ wire [31:0] wb_dat_o_rx, wb_dat_o_tx;
+ reg wb_we, wb_en_rx, wb_en_tx;
+ reg [8:0] wb_adr;
+
+ // Buffer Control
+ reg go, clear, read, write;
+ reg [3:0] buf_num;
+ wire [31:0] ctrl_word = {buf_num,3'b0,clear,write,read,step,lastline,firstline};
+ reg [8:0] firstline = 0, lastline = 0;
+ reg [3:0] step = 1;
+ reg first_tx = 1, first_rx = 1; // for verif
+
+ // TX Side
+ reg wb_we_tx;
+ wire en_tx, we_tx;
+ wire [8:0] addr_tx;
+ wire [31:0] f2r_tx, r2f_tx;
+ wire [31:0] data_tx;
+ wire read_tx, done_tx, error_tx, sop_tx, eop_tx;
+
+ wire fdone_tx, ferror_tx;
+
+ reg even;
+ reg channel_error = 0;
+
+ serdes_tx serdes_tx
+ (.clk(clk),.rst(rst),
+ .ser_tx_clk(ser_tx_clk),.ser_t(ser_t),.ser_tklsb(ser_tklsb),.ser_tkmsb(ser_tkmsb),
+ .rd_dat_i(data_tx),.rd_read_o(read_tx),.rd_done_o(done_tx),
+ .rd_error_o(error_tx),.rd_sop_i(sop_tx),.rd_eop_i(eop_tx) );
+
+ ram_2port #(.DWIDTH(32),.AWIDTH(9))
+ ram_tx(.clka(clk),.ena(wb_en_tx),.wea(wb_we_tx),.addra(wb_adr),.dia(wb_dat_i),.doa(wb_dat_o_tx),
+ .clkb(clk),.enb(en_tx),.web(we_tx),.addrb(addr_tx),.dib(f2r_tx),.dob(r2f_tx));
+
+ buffer_int #(.BUFF_NUM(1)) buffer_int_tx
+ (.clk(clk),.rst(rst),
+ .ctrl_word(ctrl_word),.go(go),
+ .done(fdone_tx),.error(ferror_tx),
+
+ .en_o(en_tx),.we_o(we_tx),.addr_o(addr_tx),.dat_to_buf(f2r_tx),.dat_from_buf(r2f_tx),
+
+ .wr_dat_i(0),.wr_write_i(0),.wr_done_i(0),
+ .wr_error_i(0),.wr_ready_o(),.wr_full_o(),
+
+ .rd_dat_o(data_tx),.rd_read_i(read_tx),.rd_done_i(done_tx),
+ .rd_error_i(error_tx),.rd_sop_o(sop_tx),.rd_eop_o(eop_tx) );
+
+
+ // RX Side
+ reg wb_we_rx;
+ wire en_rx, we_rx;
+ wire [8:0] addr_rx;
+ wire [31:0] f2r_rx, r2f_rx;
+ wire [31:0] data_rx;
+ wire write_rx, done_rx, error_rx, ready_rx, empty_rx;
+
+ wire fdone_rx, ferror_rx;
+
+ serdes_rx serdes_rx
+ (.clk(clk),.rst(rst),
+ .ser_rx_clk(ser_rx_clk),.ser_r(ser_r),.ser_rklsb(ser_rklsb),.ser_rkmsb(ser_rkmsb),
+ .wr_dat_o(data_rx),.wr_write_o(write_rx),.wr_done_o(done_rx),
+ .wr_error_o(error_rx),.wr_ready_i(ready_rx),.wr_full_i(full_rx) );
+
+ ram_2port #(.DWIDTH(32),.AWIDTH(9))
+ ram_rx(.clka(clk),.ena(wb_en_rx),.wea(wb_we_rx),.addra(wb_adr),.dia(wb_dat_i),.doa(wb_dat_o_rx),
+ .clkb(clk),.enb(en_rx),.web(we_rx),.addrb(addr_rx),.dib(f2r_rx),.dob(r2f_rx) );
+
+ buffer_int #(.BUFF_NUM(0)) buffer_int_rx
+ (.clk(clk),.rst(rst),
+ .ctrl_word(ctrl_word),.go(go),
+ .done(fdone_rx),.error(ferror_rx),
+
+ .en_o(en_rx),.we_o(we_rx),.addr_o(addr_rx),.dat_to_buf(f2r_rx),.dat_from_buf(r2f_rx),
+
+ .wr_dat_i(data_rx),.wr_write_i(write_rx),.wr_done_i(done_rx),
+ .wr_error_i(error_rx),.wr_ready_o(ready_rx),.wr_full_o(full_rx),
+
+ .rd_dat_o(),.rd_read_i(0),.rd_done_i(0),
+ .rd_error_i(0),.rd_sop_o(),.rd_eop_o() );
+
+ // Simulate the connection
+ serdes_model serdes_model
+ (.ser_tx_clk(ser_tx_clk), .ser_tkmsb(ser_tkmsb), .ser_tklsb(ser_tklsb), .ser_t(ser_t),
+ .ser_rx_clk(ser_rx_clk), .ser_rkmsb(ser_rkmsb), .ser_rklsb(ser_rklsb), .ser_r(ser_r),
+ .even(even), .error(channel_error) );
+
+ initial begin
+ wb_en_rx <= 0;
+ wb_en_tx <=0;
+ wb_we_tx <= 0;
+ wb_we_rx <= 0;
+ wb_adr <= 0;
+ wb_dat_i <= 0;
+ go <= 0;
+ even <= 0;
+ @(negedge rst);
+ @(posedge clk);
+ FillTXRAM;
+ ClearRXRAM;
+ ResetBuffer(0);
+ ResetBuffer(1);
+
+ // receive a full buffer
+ ReceiveSERDES(0,10);
+ SendSERDES(0,10);
+
+ // Receive a partial buffer
+ SendSERDES(11,20);
+ ReceiveSERDES(11,50);
+
+ // Receive too many for buffer
+ SendSERDES(21,100);
+ ReceiveSERDES(21,30);
+
+ // Send 3 packets, then wait to receive them, so they stack up in the rx fifo
+ SendSERDES(31,40);
+ SendSERDES(41,50);
+ SendSERDES(51,60);
+ repeat (10)
+ @(posedge clk);
+ ReceiveSERDES(31,40);
+ ReceiveSERDES(41,50);
+ repeat (1000)
+ @(posedge clk);
+ ReceiveSERDES(51,60);
+
+ // Overfill the FIFO, should get an error on 3rd packet
+ SendSERDES(1,400);
+ SendSERDES(1,400);
+
+
+ WaitForTX;
+ //WaitForRX;
+
+
+ repeat(1000)
+ @(posedge clk);
+ ReceiveSERDES(101,500);
+ ReceiveSERDES(101,500);
+ ReadRAM(80);
+ $finish;
+ end // initial begin
+
+ always @(posedge clk)
+ if(write_rx)
+ $display("SERDES RX, FIFO WRITE %x, FIFO RDY %d, FIFO FULL %d",data_rx, ready_rx, full_rx);
+
+ always @(posedge clk)
+ if(read_tx)
+ $display("SERDES TX, FIFO READ %x, SOP %d, EOP %d",data_tx, sop_tx, eop_tx);
+
+ initial begin
+ $dumpfile("serdes_tb.vcd");
+ $dumpvars(0,serdes_tb);
+ end
+
+ initial #10000000 $finish;
+
+ initial #259300 channel_error <= 1;
+ initial #259500 channel_error <= 0;
+
+ task FillTXRAM;
+ begin
+ wb_adr <= 0;
+ wb_dat_i <= 32'h10802000;
+ wb_we_tx <= 1;
+ wb_en_tx <= 1;
+ @(posedge clk);
+ repeat(511) begin
+ wb_dat_i <= wb_dat_i + 32'h00010001;
+ wb_adr <= wb_adr + 1;
+ @(posedge clk);
+ end // repeat (511)
+ wb_we_tx <= 0;
+ wb_en_tx <= 0;
+ @(posedge clk);
+ $display("Done entering Data into TX RAM\n");
+ end
+ endtask // FillTXRAM
+
+ task ClearRXRAM;
+ begin
+ wb_adr <= 0;
+ wb_dat_i <= 0;
+ wb_we_rx <= 1;
+ wb_en_rx <= 1;
+ wb_dat_i <= 0;
+ @(posedge clk);
+ repeat(511) begin
+ wb_adr <= wb_adr + 1;
+ @(posedge clk);
+ end // repeat (511)
+ wb_we_rx <= 0;
+ wb_en_rx <= 0;
+ @(posedge clk);
+ $display("Done clearing RX RAM\n");
+ end
+ endtask // FillRAM
+
+ task ReadRAM;
+ input [8:0] lastline;
+ begin
+ wb_en_rx <= 1;
+ wb_adr <= 0;
+ @(posedge clk);
+ @(posedge clk);
+ repeat(lastline) begin
+ $display("ADDR: %h DATA %h", wb_adr, wb_dat_o_rx);
+ wb_adr <= wb_adr + 1;
+ @(posedge clk);
+ @(posedge clk);
+ end // repeat (511)
+ $display("ADDR: %h DATA %h", wb_adr, wb_dat_o_rx);
+ wb_en_rx <= 0;
+ @(posedge clk);
+ $display("Done reading out RX RAM\n");
+ end
+ endtask // FillRAM
+
+ task ResetBuffer;
+ input [3:0] buffer_num;
+ begin
+ buf_num <= buffer_num;
+ clear <= 1; read <= 0; write <= 0;
+ go <= 1;
+ @(posedge clk);
+ go <= 0;
+ @(posedge clk);
+ $display("Buffer Reset");
+ end
+ endtask // ClearBuffer
+
+ task SetBufferWrite;
+ input [3:0] buffer_num;
+ input [8:0] start;
+ input [8:0] stop;
+ begin
+ buf_num <= buffer_num;
+ clear <= 0; read <= 0; write <= 1;
+ firstline <= start;
+ lastline <= stop;
+ go <= 1;
+ @(posedge clk);
+ go <= 0;
+ @(posedge clk);
+ $display("Buffer Set for Write");
+ end
+ endtask // SetBufferWrite
+
+ task SetBufferRead;
+ input [3:0] buffer_num;
+ input [8:0] start;
+ input [8:0] stop;
+ begin
+ buf_num <= buffer_num;
+ clear <= 0; read <= 1; write <= 0;
+ firstline <= start;
+ lastline <= stop;
+ go <= 1;
+ @(posedge clk);
+ go <= 0;
+ @(posedge clk);
+ $display("Buffer Set for Read");
+ end
+ endtask // SetBufferRead
+
+ task WaitForTX;
+ begin
+ while (!(fdone_tx | ferror_tx))
+ @(posedge clk);
+ end
+ endtask // WaitForTX
+
+ task WaitForRX;
+ begin
+ while (!(fdone_rx | ferror_rx))
+ @(posedge clk);
+ end
+ endtask // WaitForRX
+
+ task SendSERDES;
+ input [8:0] start;
+ input [8:0] stop;
+ begin
+ if(~first_tx)
+ WaitForTX;
+ else
+ first_tx <= 0;
+ ResetBuffer(1);
+ SetBufferRead(1,start,stop);
+ $display("Here");
+ end
+ endtask // SendSERDES
+
+ task ReceiveSERDES;
+ input [8:0] start;
+ input [8:0] stop;
+ begin
+ if(~first_rx)
+ WaitForRX;
+ else
+ first_rx <= 0;
+ ResetBuffer(0);
+ SetBufferWrite(0,start,stop);
+ $display("Here2");
+ end
+ endtask // ReceiveSERDES
+
+endmodule // serdes_tb
diff --git a/fpga/usrp2/serdes/serdes_tx.v b/fpga/usrp2/serdes/serdes_tx.v
new file mode 100644
index 000000000..2e5e3bd80
--- /dev/null
+++ b/fpga/usrp2/serdes/serdes_tx.v
@@ -0,0 +1,186 @@
+
+// SERDES Interface
+
+// LS-Byte is sent first, MS-Byte is second
+// Invalid K Codes
+// K0.0 000-00000 Error detected
+// K31.7 111-11111 Loss of input signal
+
+// Valid K Codes
+// K28.0 000-11100
+// K28.1 001-11100 Alternate COMMA?
+// K28.2 010-11100
+// K28.3 011-11100
+// K28.4 100-11100
+// K28.5 101-11100 Standard COMMA?
+// K28.6 110-11100
+// K28.7 111-11100 Bad COMMA?
+// K23.7 111-10111
+// K27.7 111-11011
+// K29.7 111-11101
+// K30.7 111-11110
+
+module serdes_tx
+ #(parameter FIFOSIZE = 9)
+ (input clk,
+ input rst,
+
+ // TX HW Interface
+ output ser_tx_clk,
+ output reg [15:0] ser_t,
+ output reg ser_tklsb,
+ output reg ser_tkmsb,
+
+ // TX Stream Interface
+ input [31:0] rd_dat_i,
+ input [3:0] rd_flags_i,
+ output rd_ready_o,
+ input rd_ready_i,
+
+ // Flow control interface
+ input inhibit_tx,
+ input send_xon,
+ input send_xoff,
+ output sent,
+
+ // FIFO Levels
+ output [15:0] fifo_occupied,
+ output fifo_full,
+ output fifo_empty,
+
+ // DEBUG
+ output [31:0] debug
+ );
+
+ localparam K_COMMA = 8'b101_11100; // 0xBC K28.5
+ localparam K_IDLE = 8'b001_11100; // 0x3C K28.1
+ localparam K_PKT_START = 8'b110_11100; // 0xDC K28.6
+ localparam K_PKT_END = 8'b100_11100; // 0x9C K28.4
+ localparam K_XON = 8'b010_11100; // 0x5C K28.2
+ localparam K_XOFF = 8'b011_11100; // 0x7C K28.3
+ localparam K_LOS = 8'b111_11111; // 0xFF K31.7
+ localparam K_ERROR = 8'b000_00000; // 0x00 K00.0
+ localparam D_56 = 8'b110_00101; // 0xC5 D05.6
+ assign ser_tx_clk = clk;
+
+ localparam IDLE = 3'd0;
+ localparam RUN1 = 3'd1;
+ localparam RUN2 = 3'd2;
+ localparam DONE = 3'd3;
+ localparam SENDCRC = 3'd4;
+ localparam WAIT = 3'd5;
+
+ reg [2:0] state;
+
+ reg [15:0] CRC;
+ wire [15:0] nextCRC;
+ reg [3:0] wait_count;
+
+ // Internal FIFO, size 9 is 2K, size 10 is 4K bytes
+ wire sop_o, eop_o;
+ wire [31:0] data_o;
+
+ wire rd_sop_i = rd_flags_i[0];
+ wire rd_eop_i = rd_flags_i[1];
+ wire [1:0] rd_occ_i = rd_flags_i[3:2]; // Unused
+
+ wire have_data, empty, read;
+ fifo_cascade #(.WIDTH(34),.SIZE(FIFOSIZE)) serdes_tx_fifo
+ (.clk(clk),.reset(rst),.clear(0),
+ .datain({rd_sop_i,rd_eop_i,rd_dat_i}), .src_rdy_i(rd_ready_i), .dst_rdy_o(rd_ready_o),
+ .dataout({sop_o,eop_o,data_o}), .dst_rdy_i(read), .src_rdy_o(have_data),
+ .space(), .occupied(fifo_occupied) );
+
+ assign fifo_full = ~rd_ready_o;
+ assign empty = ~have_data;
+ assign fifo_empty = empty;
+
+ // FIXME Implement flow control
+ reg [15:0] second_word;
+ reg [33:0] pipeline;
+
+ assign read = (~send_xon & ~send_xoff & (state==RUN2)) | ((state==IDLE) & ~empty & ~sop_o);
+ assign sent = send_xon | send_xoff;
+ // 2nd half of above probably not necessary. Just in case we get junk between packets
+
+ always @(posedge clk)
+ if(rst)
+ begin
+ state <= IDLE;
+ wait_count <= 0;
+ {ser_tkmsb,ser_tklsb,ser_t} <= 18'd0;
+ //{2'b10,K_COMMA,K_COMMA};
+ // make tkmsb and tklsb different so they can go in IOFFs
+ end
+ else
+ if(send_xon)
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b11,K_XON,K_XON};
+ else if(send_xoff)
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b11,K_XOFF,K_XOFF};
+ else
+ case(state)
+ IDLE :
+ begin
+ if(sop_o & ~empty & ~inhibit_tx)
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b11,K_PKT_START,K_PKT_START};
+ state <= RUN1;
+ end
+ else
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b10,K_COMMA,D_56};
+ end
+ RUN1 :
+ begin
+ if(empty | inhibit_tx)
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b10,K_COMMA,D_56};
+ else
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b00,data_o[15:0]};
+ state <= RUN2;
+ end
+ end
+ RUN2 :
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b00,data_o[31:16]};
+ if(eop_o)
+ state <= DONE;
+ else
+ state <= RUN1;
+ end
+ DONE :
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b11,K_PKT_END,K_PKT_END};
+ state <= SENDCRC;
+ end
+ SENDCRC :
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b00,CRC};
+ state <= WAIT;
+ wait_count <= 4'd15;
+ end
+ WAIT :
+ begin
+ {ser_tkmsb,ser_tklsb,ser_t} <= {2'b10,K_COMMA,D_56};
+ if(wait_count == 0)
+ state <= IDLE;
+ else
+ wait_count <= wait_count - 1;
+ end
+ default
+ state <= IDLE;
+ endcase // case(state)
+
+ always @(posedge clk)
+ if(rst)
+ CRC <= 16'hFFFF;
+ else if(state == IDLE)
+ CRC <= 16'hFFFF;
+ else if( (~empty & ~inhibit_tx & (state==RUN1)) || (state==RUN2) )
+ CRC <= nextCRC;
+
+ CRC16_D16 crc_blk( (state==RUN1) ? data_o[15:0] : data_o[31:16], CRC, nextCRC);
+
+ assign debug = { 28'd0, state[2:0] };
+
+endmodule // serdes_tx
+