diff options
-rw-r--r-- | simple_gemac/crc.v | 63 | ||||
-rw-r--r-- | simple_gemac/flow_ctrl_rx.v | 85 | ||||
-rw-r--r-- | simple_gemac/flow_ctrl_tx.v | 36 | ||||
-rw-r--r-- | simple_gemac/simple_gemac.v | 55 | ||||
-rw-r--r-- | simple_gemac/simple_gemac_tb.v | 88 | ||||
-rw-r--r-- | simple_gemac/simple_gemac_tx.v | 200 |
6 files changed, 527 insertions, 0 deletions
diff --git a/simple_gemac/crc.v b/simple_gemac/crc.v new file mode 100644 index 000000000..c54623859 --- /dev/null +++ b/simple_gemac/crc.v @@ -0,0 +1,63 @@ +
+module crc
+ (input clk,
+ input reset,
+ input clear,
+ input [7:0] data,
+ input calc,
+ output [31:0] crc_out);
+
+ function[31:0] NextCRC;
+ input[7:0] D;
+ input[31:0] C;
+ reg[31:0] NewCRC;
+ begin
+ NewCRC[0] = C[24]^C[30]^D[1]^D[7];
+ NewCRC[1] = C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[2] = C[26]^D[5]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[3] = C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];
+ NewCRC[4] = C[28]^D[3]^C[27]^D[4]^C[26]^D[5]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[5] = C[29]^D[2]^C[28]^D[3]^C[27]^D[4]^C[25]^C[31]^D[0]^D[6]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[6] = C[30]^D[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];
+ NewCRC[7] = C[31]^D[0]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7];
+ NewCRC[8] = C[0]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7];
+ NewCRC[9] = C[1]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6];
+ NewCRC[10] = C[2]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[24]^D[7];
+ NewCRC[11] = C[3]^C[28]^D[3]^C[27]^D[4]^C[25]^D[6]^C[24]^D[7];
+ NewCRC[12] = C[4]^C[29]^D[2]^C[28]^D[3]^C[26]^D[5]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[13] = C[5]^C[30]^D[1]^C[29]^D[2]^C[27]^D[4]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];
+ NewCRC[14] = C[6]^C[31]^D[0]^C[30]^D[1]^C[28]^D[3]^C[27]^D[4]^C[26]^D[5];
+ NewCRC[15] = C[7]^C[31]^D[0]^C[29]^D[2]^C[28]^D[3]^C[27]^D[4];
+ NewCRC[16] = C[8]^C[29]^D[2]^C[28]^D[3]^C[24]^D[7];
+ NewCRC[17] = C[9]^C[30]^D[1]^C[29]^D[2]^C[25]^D[6];
+ NewCRC[18] = C[10]^C[31]^D[0]^C[30]^D[1]^C[26]^D[5];
+ NewCRC[19] = C[11]^C[31]^D[0]^C[27]^D[4];
+ NewCRC[20] = C[12]^C[28]^D[3];
+ NewCRC[21] = C[13]^C[29]^D[2];
+ NewCRC[22] = C[14]^C[24]^D[7];
+ NewCRC[23] = C[15]^C[25]^D[6]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[24] = C[16]^C[26]^D[5]^C[25]^C[31]^D[0]^D[6];
+ NewCRC[25] = C[17]^C[27]^D[4]^C[26]^D[5];
+ NewCRC[26] = C[18]^C[28]^D[3]^C[27]^D[4]^C[24]^C[30]^D[1]^D[7];
+ NewCRC[27] = C[19]^C[29]^D[2]^C[28]^D[3]^C[25]^C[31]^D[0]^D[6];
+ NewCRC[28] = C[20]^C[30]^D[1]^C[29]^D[2]^C[26]^D[5];
+ NewCRC[29] = C[21]^C[31]^D[0]^C[30]^D[1]^C[27]^D[4];
+ NewCRC[30] = C[22]^C[31]^D[0]^C[28]^D[3];
+ NewCRC[31] = C[23]^C[29]^D[2];
+ NextCRC = NewCRC;
+ end
+ endfunction
+
+ reg [31:0] crc_reg;
+ always @ (posedge clk)
+ if (reset | clear)
+ crc_reg <= 32'hffffffff;
+ else if (calc)
+ crc_reg <= NextCRC(data,crc_reg);
+
+ assign crc_out = ~{crc_reg[24],crc_reg[25],crc_reg[26],crc_reg[27],crc_reg[28],crc_reg[29],crc_reg[30],crc_reg[31],
+ crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23],
+ crc_reg[8],crc_reg[9],crc_reg[10],crc_reg[11],crc_reg[12],crc_reg[13],crc_reg[14],crc_reg[15],
+ crc_reg[0],crc_reg[1],crc_reg[2],crc_reg[3],crc_reg[4],crc_reg[5],crc_reg[6],crc_reg[7] };
+
+endmodule // crc
diff --git a/simple_gemac/flow_ctrl_rx.v b/simple_gemac/flow_ctrl_rx.v new file mode 100644 index 000000000..7ded9e08b --- /dev/null +++ b/simple_gemac/flow_ctrl_rx.v @@ -0,0 +1,85 @@ +
+// RX side of flow control -- when we are running out of RX space, send a PAUSE
+
+module flow_ctrl_rx
+ (input rst,
+ //host processor
+ input pause_frame_send_en,
+ input [15:0] pause_quanta_set,
+ input [15:0] fc_hwmark,
+ input [15:0] fc_lwmark,
+ // From MAC_rx_ctrl
+ input rx_clk,
+ input [15:0] rx_fifo_space,
+ // MAC_tx_ctrl
+ input tx_clk,
+ output reg xoff_gen,
+ output reg xon_gen,
+ input xoff_gen_complete,
+ input xon_gen_complete
+ );
+
+ // ******************************************************************************
+ // Force our TX to send a PAUSE frame because our RX is nearly full
+ // ******************************************************************************
+
+ reg xon_int, xoff_int;
+ reg [21:0] countdown;
+
+ always @(posedge rx_clk or posedge rst)
+ if(rst)
+ begin
+ xon_int <= 0;
+ xoff_int <= 0;
+ end
+ else
+ begin
+ xon_int <= 0;
+ xoff_int <= 0;
+ if(pause_frame_send_en)
+ if(countdown == 0)
+ if(rx_fifo_space < fc_lwmark)
+ xoff_int <= 1;
+ else
+ ;
+ else
+ if(rx_fifo_space > fc_hwmark)
+ xon_int <= 1;
+ end // else: !if(rst)
+
+ reg xoff_int_d1, xon_int_d1;
+
+ always @(posedge rx_clk)
+ xon_int_d1 <= xon_int;
+ always @(posedge rx_clk)
+ xoff_int_d1 <= xoff_int;
+
+ always @ (posedge tx_clk or posedge rst)
+ if (rst)
+ xoff_gen <=0;
+ else if (xoff_gen_complete)
+ xoff_gen <=0;
+ else if (xoff_int | xoff_int_d1)
+ xoff_gen <=1;
+
+ always @ (posedge tx_clk or posedge rst)
+ if (rst)
+ xon_gen <=0;
+ else if (xon_gen_complete)
+ xon_gen <=0;
+ else if (xon_int | xon_int_d1)
+ xon_gen <=1;
+
+ wire [15:0] pq_reduced = pause_quanta_set - 2;
+
+ always @(posedge tx_clk or posedge rst)
+ if(rst)
+ countdown <= 0;
+ else if(xoff_gen)
+ countdown <= {pq_reduced,6'd0};
+ else if(xon_gen)
+ countdown <= 0;
+ else if(countdown != 0)
+ countdown <= countdown - 1;
+
+endmodule // flow_ctrl
diff --git a/simple_gemac/flow_ctrl_tx.v b/simple_gemac/flow_ctrl_tx.v new file mode 100644 index 000000000..9f7556de4 --- /dev/null +++ b/simple_gemac/flow_ctrl_tx.v @@ -0,0 +1,36 @@ +
+// TX side of flow control -- when other side sends PAUSE, we wait
+
+module flow_ctrl_tx
+ (input rst,
+ input tx_clk,
+ //host processor
+ input tx_pause_en,
+ // From MAC_rx_ctrl
+ input [15:0] pause_quanta,
+ input pause_quanta_val,
+ // MAC_tx_ctrl
+ output pause_apply,
+ input pause_quanta_sub);
+
+ // ******************************************************************************
+ // Inhibit our TX from transmitting because they sent us a PAUSE frame
+ // ******************************************************************************
+
+ reg [15:0] pause_quanta_counter;
+ reg pqval_d1, pqval_d2;
+
+ always @(posedge tx_clk) pqval_d1 <= pause_quanta_val;
+ always @(posedge tx_clk) pqval_d2 <= pqval_d1;
+
+ always @ (posedge tx_clk or posedge rst)
+ if (rst)
+ pause_quanta_counter <= 0;
+ else if (pqval_d1 & ~pqval_d2)
+ pause_quanta_counter <= pause_quanta;
+ else if((pause_quanta_counter!=0) & pause_quanta_sub)
+ pause_quanta_counter <= pause_quanta_counter - 1;
+
+ assign pause_apply = tx_pause_en & (pause_quanta_counter != 0);
+
+endmodule // flow_ctrl
diff --git a/simple_gemac/simple_gemac.v b/simple_gemac/simple_gemac.v new file mode 100644 index 000000000..0769fe615 --- /dev/null +++ b/simple_gemac/simple_gemac.v @@ -0,0 +1,55 @@ + +module simple_gemac + (input clk125, input reset, + // GMII + output GMII_GTX_CLK, output GMII_TX_EN, output GMII_TX_ER, output [7:0] GMII_TXD, + input GMII_RX_CLK, input GMII_RX_DV, input GMII_RX_ER, input [7:0] GMII_RXD, + + // Flow Control Interface + input pause_req, input [15:0] pause_time, + + // RX Client Interface + output rx_clk, output [7:0] rx_data, output rx_valid, output rx_error, output rx_ack, + + // TX Client Interface + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack + ); + + localparam SGE_IFG = 8'd12; // 12 should be the absolute minimum + localparam SGE_RESPECT_FLOW_CTRL = 1'b1; // stop sending if other side requests + + wire rst_rxclk, rst_txclk; + oneshot_2clk tx_rst_1shot (.clk_in(tx_clk),.in(reset),.clk_out(tx_clk),.out(rst_txclk)); // FIXME clocks + oneshot_2clk rx_rst_1shot (.clk_in(sys_clk),.in(reset),.clk_out(rx_clk),.out(rst_rxclk)); + + wire [15:0] pause_quanta_rcvd; + + simple_gemac_tx simple_gemac_tx + (.clk125(clk125),.reset(rst_txclk), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .tx_clk(tx_clk), .tx_data(tx_data), .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack), + .ifg(SGE_IFG), .mac_addr(48'hF1_F2_F3_F4_F5_F6), + .pause_req(pause_req), .pause_time(pause_time), // We request flow control + .pause_apply(pause_apply), .pause_applied(pause_applied) // We respect flow control + ); +/* + simple_gemac_rx simple_gemac_rx + (.reset(rst_rxclk), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .rx_clk(rx_clk), .rx_data(rx_data), .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .pause_quanta_rcvd(pause_qanta_rcvd), .pause_rcvd(pause_rcvd) + ); + */ + flow_ctrl_tx flow_ctrl_tx + (.rst(reset_txclk), .tx_clk(tx_clk), + .tx_pause_en(SGE_RESPECT_FLOW_CTRL), + .pause_quanta(pause_quanta_rcvd), // 16 bit value + .pause_quanta_val(pause_rcvd), + .pause_apply(pause_apply), + .pause_quanta_sub(pause_applied) + ); + + +endmodule // simple_gemac diff --git a/simple_gemac/simple_gemac_tb.v b/simple_gemac/simple_gemac_tb.v new file mode 100644 index 000000000..61dbbe4c9 --- /dev/null +++ b/simple_gemac/simple_gemac_tb.v @@ -0,0 +1,88 @@ + + +module simple_gemac_tb; + + + reg clk = 0; + reg reset = 1; + + initial #1000 reset = 0; + always #50 clk = ~clk; + + wire GMII_RX_DV, GMII_RX_ER, GMII_TX_EN, GMII_TX_ER; + wire [7:0] GMII_RXD, GMII_TXD; + + wire rx_valid, rx_error, rx_ack; + wire tx_ack; + reg tx_valid = 0, tx_error = 0; + + wire [7:0] rx_data; + reg [7:0] tx_data; + + wire [15:0] pause_time = 16'hBEEF; + reg pause_req = 0; + + simple_gemac simple_gemac + (.clk125(clk), .reset(reset), + .GMII_GTX_CLK(GMII_GTX_CLK), .GMII_TX_EN(GMII_TX_EN), + .GMII_TX_ER(GMII_TX_ER), .GMII_TXD(GMII_TXD), + .GMII_RX_CLK(GMII_RX_CLK), .GMII_RX_DV(GMII_RX_DV), + .GMII_RX_ER(GMII_RX_ER), .GMII_RXD(GMII_RXD), + .pause_req(pause_req), .pause_time(pause_time), + .rx_clk(rx_clk), .rx_data(rx_data), + .rx_valid(rx_valid), .rx_error(rx_error), .rx_ack(rx_ack), + .tx_clk(tx_clk), .tx_data(tx_data), + .tx_valid(tx_valid), .tx_error(tx_error), .tx_ack(tx_ack) + ); + + task SendFlowCtrl; + begin + $display("Sending Flow Control"); + @(posedge clk); + pause_req <= 1; + @(posedge clk); + pause_req <= 0; + end + endtask // SendFlowCtrl + + reg [31:0] count; + task SendPacket; + input [7:0] data_start; + input [31:0] data_len; + begin + $display("Sending Packet"); + count <= 0; + tx_data <= data_start; + tx_error <= 0; + tx_valid <= 1; + while(~tx_ack) + @(posedge tx_clk); + while(count < data_len) + begin + tx_data <= tx_data + 1; + count <= count + 1; + @(posedge clk); + end + tx_valid <= 0; + @(posedge tx_clk); + end + endtask // SendPacket + + initial $dumpfile("simple_gemac_tb.vcd"); + initial $dumpvars(0,simple_gemac_tb); + + initial + begin + @(negedge reset); + repeat (20) + @(posedge clk); + SendFlowCtrl; + repeat (100) + @(posedge clk); + SendPacket(8'hAA,10); + repeat (1000) + @(posedge clk); + $finish; + end + +endmodule // simple_gemac_tb diff --git a/simple_gemac/simple_gemac_tx.v b/simple_gemac/simple_gemac_tx.v new file mode 100644 index 000000000..cddb18a3a --- /dev/null +++ b/simple_gemac/simple_gemac_tx.v @@ -0,0 +1,200 @@ + +module simple_gemac_tx + (input clk125, input reset, + output GMII_GTX_CLK, output reg GMII_TX_EN, output reg GMII_TX_ER, output reg [7:0] GMII_TXD, + output tx_clk, input [7:0] tx_data, input tx_valid, input tx_error, output tx_ack, + input [7:0] ifg, input [47:0] mac_addr, + input pause_req, input [15:0] pause_time, + input pause_apply, output pause_applied + ); + + assign GMII_GTX_CLK = clk125; + assign tx_clk = clk125; + + reg [7:0] tx_state; + reg [7:0] ifg_ctr; + reg [15:0] frame_len_ctr; + reg [7:0] pause_ctr, pause_dat; + + wire in_ifg = (ifg_ctr != 0); + + wire [31:0] crc_out; + + localparam MIN_FRAME_LEN = 64 + 8 - 4; // Min frame length includes preamble but not CRC + localparam MAX_FRAME_LEN = 8192; // How big are the jumbo frames we want to handle? + always @(posedge tx_clk) + if(reset |(tx_state <= TX_IDLE)) + frame_len_ctr <= 0; + else + frame_len_ctr <= frame_len_ctr + 1; + + localparam TX_IDLE = 0; + localparam TX_PREAMBLE = 1; + localparam TX_SOF_DEL = TX_PREAMBLE + 7; + localparam TX_IN_FRAME = TX_SOF_DEL + 1; + localparam TX_IN_FRAME_2 = TX_IN_FRAME + 1; + localparam TX_PAD = TX_IN_FRAME_2 + 1; + localparam TX_CRC_0 = 16; + localparam TX_CRC_1 = TX_CRC_0 + 1; + localparam TX_CRC_2 = TX_CRC_0 + 2; + localparam TX_CRC_3 = TX_CRC_0 + 3; + localparam TX_ERROR = 32; + localparam TX_PAUSE = 56; + localparam TX_PAUSE_PRE = TX_PAUSE + 1; + localparam TX_PAUSE_SOF = 64; + localparam TX_PAUSE_END = TX_PAUSE_SOF + 18; + + reg send_pause; + reg [15:0] pause_time_held; + + always @(posedge tx_clk) + if(reset) + send_pause <= 0; + else if(pause_req) + send_pause <= 1; + else if(tx_state == TX_PAUSE) + send_pause <= 0; + + always @(posedge tx_clk) + if(pause_req) + pause_time_held <= pause_time; + + always @(posedge tx_clk) + if(reset) + tx_state <= TX_IDLE; + else + case(tx_state) + TX_IDLE : + if(~in_ifg) + if(send_pause) + tx_state <= TX_PAUSE; + else if(tx_valid) + tx_state <= TX_PREAMBLE; + TX_IN_FRAME : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_PAD; + else if(frame_len_ctr == MIN_FRAME_LEN) + tx_state <= TX_IN_FRAME_2; + TX_IN_FRAME_2 : + if(tx_error) + tx_state <= TX_ERROR; // underrun + else if(~tx_valid) + tx_state <= TX_CRC_0; + TX_PAD : + if(frame_len_ctr == MIN_FRAME_LEN) + tx_state <= TX_CRC_0; + TX_CRC_3 : + tx_state <= TX_IDLE; + TX_ERROR : + tx_state <= TX_IDLE; + TX_PAUSE : + tx_state <= TX_PAUSE_PRE; + TX_PAUSE_END : + tx_state <= TX_PAD; + default : + tx_state <= tx_state + 1; + endcase // case (tx_state) + + always @(posedge tx_clk) + if(reset) + begin + GMII_TX_EN <= 0; + GMII_TX_ER <= 0; + GMII_TXD <= 0; + end + else + casex(tx_state) + TX_IDLE : + begin + GMII_TX_EN <= 0; + GMII_TX_ER <= 0; + GMII_TXD <= 0; + end + TX_PREAMBLE, TX_PAUSE_PRE : + begin + GMII_TXD <= 8'h55; + GMII_TX_EN <= 1; + end + TX_SOF_DEL, TX_PAUSE_SOF : + GMII_TXD <= 8'hD5; + TX_IN_FRAME, TX_IN_FRAME_2 : + GMII_TXD <= tx_data; + TX_ERROR : + begin + GMII_TX_ER <= 1; + GMII_TXD <= 0; + end + TX_CRC_0 : + GMII_TXD <= crc_out[31:24]; + TX_CRC_1 : + GMII_TXD <= crc_out[23:16]; + TX_CRC_2 : + GMII_TXD <= crc_out[15:8]; + TX_CRC_3 : + GMII_TXD <= crc_out[7:0]; + TX_PAD : + GMII_TXD <= 0; + 8'b01xx_xxxx : // In Pause Frame + GMII_TXD <= pause_dat; + endcase // case (tx_state) + + localparam SGE_FLOW_CTRL_ADDR = 48'h01_80_C2_00_00_01; + always @(posedge tx_clk) + case(tx_state) + TX_PAUSE_SOF : + pause_dat <= SGE_FLOW_CTRL_ADDR[47:40]; // Note everything must be 1 cycle early + TX_PAUSE_SOF + 1: + pause_dat <= SGE_FLOW_CTRL_ADDR[39:32]; + TX_PAUSE_SOF + 2: + pause_dat <= SGE_FLOW_CTRL_ADDR[31:24]; + TX_PAUSE_SOF + 3: + pause_dat <= SGE_FLOW_CTRL_ADDR[23:16]; + TX_PAUSE_SOF + 4: + pause_dat <= SGE_FLOW_CTRL_ADDR[15:8]; + TX_PAUSE_SOF + 5: + pause_dat <= SGE_FLOW_CTRL_ADDR[7:0]; + TX_PAUSE_SOF + 6: + pause_dat <= mac_addr[47:40]; + TX_PAUSE_SOF + 7: + pause_dat <= mac_addr[39:32]; + TX_PAUSE_SOF + 8: + pause_dat <= mac_addr[31:24]; + TX_PAUSE_SOF + 9: + pause_dat <= mac_addr[23:16]; + TX_PAUSE_SOF + 10: + pause_dat <= mac_addr[15:8]; + TX_PAUSE_SOF + 11: + pause_dat <= mac_addr[7:0]; + TX_PAUSE_SOF + 12: + pause_dat <= 8'h88; // Type = 8808 = MAC ctrl frame + TX_PAUSE_SOF + 13: + pause_dat <= 8'h08; + TX_PAUSE_SOF + 14: + pause_dat <= 8'h00; // Opcode = 0001 = PAUSE + TX_PAUSE_SOF + 15: + pause_dat <= 8'h01; + TX_PAUSE_SOF + 16: + pause_dat <= pause_time_held[15:8]; + TX_PAUSE_SOF + 17: + pause_dat <= pause_time_held[7:0]; + endcase // case (tx_state) + + wire start_ifg = (tx_state == TX_CRC_3); + always @(posedge tx_clk) + if(reset) + ifg_ctr <= 100; + else if(start_ifg) + ifg_ctr <= ifg; + else if(ifg_ctr != 0) + ifg_ctr <= ifg_ctr - 1; + + wire clear_crc = (tx_state == TX_IDLE); + wire calc_crc = 1; + + crc crc(.clk(tx_clk), .reset(reset), .clear(clear_crc), + .data(GMII_TXD), .calc(calc_crc), .crc_out(crc_out)); + + assign tx_ack = (tx_state == TX_IN_FRAME); +endmodule // simple_gemac_tx |