commit 536546cc793b0f8c6bd59acb4ffd1c037ed26cd8
parent bfe5f93a69c066247a6225d3fadbf8775116fdaa
Author: Brian Swetland <swetland@frotz.net>
Date: Sat, 19 Jul 2014 23:32:23 -0700
ethernet building blocks
Diffstat:
6 files changed, 529 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -45,6 +45,17 @@ MODULE_SRCS += hdl/axi_sram.sv
MODULE_SRCS += hdl/axi_pattern_writer.sv
include build/verilator-sim.mk
+MODULE_NAME := eth-crc32-test
+MODULE_SRCS := hdl/test/eth_crc32_test.sv
+MODULE_SRCS += hdl/eth_crc32.sv
+include build/verilator-sim.mk
+
+MODULE_NAME := eth-rmii-test
+MODULE_SRCS := hdl/test/eth_rmii_test.sv
+MODULE_SRCS += hdl/eth_rmii_tx.sv
+MODULE_SRCS += hdl/eth_rmii_rx.sv
+include build/verilator-sim.mk
+
clean::
rm -rf sim synth out
diff --git a/hdl/eth_crc32.sv b/hdl/eth_crc32.sv
@@ -0,0 +1,66 @@
+`timescale 1ns / 1ps
+
+module eth_crc32(
+ input clk,
+ input en,
+ input rst,
+ input [7:0]dat,
+ output [31:0]crc
+ );
+
+reg [31:0]c = 32'hFFFFFFFF;
+reg [31:0]nxt;
+
+wire [7:0]d = { dat[0],dat[1],dat[2],dat[3],dat[4],dat[5],dat[6],dat[7] };
+
+assign crc = {
+ c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],
+ c[8],c[9],c[10],c[11],c[12],c[13],c[14],c[15],
+ c[16],c[17],c[18],c[19],c[20],c[21],c[22],c[23],
+ c[24],c[25],c[26],c[27],c[28],c[29],c[30],c[31]
+ };
+
+always_comb begin
+ nxt[0] = c[24]^c[30]^d[0]^d[6];
+ nxt[1] = c[24]^c[25]^c[30]^c[31]^d[0]^d[1]^d[6]^d[7];
+ nxt[2] = c[24]^c[25]^c[26]^c[30]^c[31]^d[0]^d[1]^d[2]^d[6]^d[7];
+ nxt[3] = c[25]^c[26]^c[27]^c[31]^d[1]^d[2]^d[3]^d[7];
+ nxt[4] = c[24]^c[26]^c[27]^c[28]^c[30]^d[0]^d[2]^d[3]^d[4]^d[6];
+ nxt[5] = c[24]^c[25]^c[27]^c[28]^c[29]^c[30]^c[31]^d[0]^d[1]^d[3]^d[4]^d[5]^d[6]^d[7];
+ nxt[6] = c[25]^c[26]^c[28]^c[29]^c[30]^c[31]^d[1]^d[2]^d[4]^d[5]^d[6]^d[7];
+ nxt[7] = c[24]^c[26]^c[27]^c[29]^c[31]^d[0]^d[2]^d[3]^d[5]^d[7];
+ nxt[8] = c[0]^c[24]^c[25]^c[27]^c[28]^d[0]^d[1]^d[3]^d[4];
+ nxt[9] = c[1]^c[25]^c[26]^c[28]^c[29]^d[1]^d[2]^d[4]^d[5];
+ nxt[10] = c[2]^c[24]^c[26]^c[27]^c[29]^d[0]^d[2]^d[3]^d[5];
+ nxt[11] = c[3]^c[24]^c[25]^c[27]^c[28]^d[0]^d[1]^d[3]^d[4];
+ nxt[12] = c[4]^c[24]^c[25]^c[26]^c[28]^c[29]^c[30]^d[0]^d[1]^d[2]^d[4]^d[5]^d[6];
+ nxt[13] = c[5]^c[25]^c[26]^c[27]^c[29]^c[30]^c[31]^d[1]^d[2]^d[3]^d[5]^d[6]^d[7];
+ nxt[14] = c[6]^c[26]^c[27]^c[28]^c[30]^c[31]^d[2]^d[3]^d[4]^d[6]^d[7];
+ nxt[15] = c[7]^c[27]^c[28]^c[29]^c[31]^d[3]^d[4]^d[5]^d[7];
+ nxt[16] = c[8]^c[24]^c[28]^c[29]^d[0]^d[4]^d[5];
+ nxt[17] = c[9]^c[25]^c[29]^c[30]^d[1]^d[5]^d[6];
+ nxt[18] = c[10]^c[26]^c[30]^c[31]^d[2]^d[6]^d[7];
+ nxt[19] = c[11]^c[27]^c[31]^d[3]^d[7];
+ nxt[20] = c[12]^c[28]^d[4];
+ nxt[21] = c[13]^c[29]^d[5];
+ nxt[22] = c[14]^c[24]^d[0];
+ nxt[23] = c[15]^c[24]^c[25]^c[30]^d[0]^d[1]^d[6];
+ nxt[24] = c[16]^c[25]^c[26]^c[31]^d[1]^d[2]^d[7];
+ nxt[25] = c[17]^c[26]^c[27]^d[2]^d[3];
+ nxt[26] = c[18]^c[24]^c[27]^c[28]^c[30]^d[0]^d[3]^d[4]^d[6];
+ nxt[27] = c[19]^c[25]^c[28]^c[29]^c[31]^d[1]^d[4]^d[5]^d[7];
+ nxt[28] = c[20]^c[26]^c[29]^c[30]^d[2]^d[5]^d[6];
+ nxt[29] = c[21]^c[27]^c[30]^c[31]^d[3]^d[6]^d[7];
+ nxt[30] = c[22]^c[28]^c[31]^d[4]^d[7];
+ nxt[31] = c[23]^c[29]^d[5];
+end
+
+always @(posedge clk) begin
+ if (rst) begin
+ c <= 32'hFFFFFFFF;
+ end else if (en) begin
+ c <= nxt;
+ end
+end
+
+endmodule
diff --git a/hdl/eth_rmii_rx.sv b/hdl/eth_rmii_rx.sv
@@ -0,0 +1,141 @@
+/* Copyright 2014 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+`timescale 1ns / 1ps
+
+// CRS_DV -- multiplexed, CR on first di-bit, DV on second di-bit of each nibble
+//
+// preamble | packet
+// crs_dv ... 1 1 1 1 1 CR DV CR DV CR DV CR ...
+// rx0 ... 0 0 0 0 1 b0 b2 b4 b6 b0 b2 b4 ...
+// rx1 ... 1 1 1 1 1 b1 b3 b5 b7 b1 b3 b5 ...
+//
+// CR can go low when carrier lost, while DV remains asserted through end of frame.
+
+// valid is asserted on each clock where data contains a byte of the frame
+// eop is asserted for one clock after the last byte of the frame has arrived
+// and before the next frame's first byte arrives
+
+module eth_rmii_rx(
+ input clk50,
+
+ input rx0,
+ input rx1,
+ input crs_dv,
+
+ output reg [7:0]data = 0,
+ output reg valid = 0,
+ output reg eop = 0
+ );
+
+typedef enum {
+ IDLE, PRE1, PRE2, PRE3,
+ DAT0, DAT1, DAT2, DAT3,
+ ERR0, ERR1, EOP
+} state_t;
+
+state_t state = IDLE;
+state_t next_state;
+
+reg [7:0]next_data;
+reg next_valid;
+reg next_eop;
+
+wire [7:0]rxshift = { rx1, rx0, data[7:2] };
+
+wire [1:0]rxd = { rx1, rx0 };
+
+always_comb begin
+ next_state = state;
+ next_data = data;
+ next_valid = 0;
+ next_eop = 0;
+
+ case (state)
+ IDLE: if ((rxd == 2'b01) && (crs_dv == 1)) begin
+ // crs_dv may go high asynchronously
+ // only move to preamble on crs_dv AND a preamble di-bit
+ next_state = PRE1;
+ end
+ PRE1: if (rxd == 2'b01) begin
+ next_state = PRE2;
+ end else begin
+ next_state = ERR0;
+ end
+ PRE2: if (rxd == 2'b01) begin
+ next_state = PRE3;
+ end else begin
+ next_state = ERR0;
+ end
+ PRE3: if (rxd == 2'b11) begin
+ next_state = DAT0;
+ end else if (rxd == 2'b01) begin
+ next_state = PRE3;
+ end else begin
+ next_state = ERR0;
+ end
+ DAT0: begin
+ next_data = rxshift;
+ next_state = DAT1;
+ end
+ DAT1: begin
+ next_data = rxshift;
+ if (crs_dv) begin
+ next_state = DAT2;
+ end else begin
+ next_state = EOP;
+ end
+ end
+ DAT2: begin
+ next_data = rxshift;
+ next_state = DAT3;
+ end
+ DAT3: begin
+ next_data = rxshift;
+ if (crs_dv) begin
+ next_state = DAT0;
+ next_valid = 1;
+ end else begin
+ next_state = EOP;
+ end
+ end
+ EOP: begin
+ next_state = IDLE;
+ next_data = 0;
+ next_eop = 1;
+ end
+ ERR0: begin
+ if (crs_dv == 0) begin
+ next_state = ERR1;
+ end
+ end
+ ERR1: begin
+ if (crs_dv == 0) begin
+ next_state = IDLE;
+ end else begin
+ next_state = ERR0;
+ end
+ end
+ endcase
+end
+
+always_ff @(posedge clk50) begin
+ state <= next_state;
+ valid <= next_valid;
+ data <= next_data;
+ eop <= next_eop;
+end
+
+endmodule
diff --git a/hdl/eth_rmii_tx.sv b/hdl/eth_rmii_tx.sv
@@ -0,0 +1,146 @@
+/* Copyright 2014 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+`timescale 1ns / 1ps
+
+// 1. Asserting packet starts tx cycle.
+// 2. Deasserting packet will cause tx cycle to complete after current byte
+// has completed transmission.
+// 3. busy is asserted while a packet is in flight (and will remain asserted after
+// packet deasserts, until after inter-packet-gap (IPG) has completed).
+// 4. advance is asserted when txdata is consumed
+
+module eth_rmii_tx(
+ input clk50,
+
+ output tx0,
+ output tx1,
+ output reg txen = 0,
+
+ input [7:0]data,
+ input packet,
+ output reg busy = 0,
+ output reg advance = 0
+ );
+
+typedef enum {
+ IDLE, PRE, DAT0, DAT1, DAT2, DAT3, EOP
+} state_t;
+
+state_t state = IDLE;
+state_t next_state;
+
+reg [7:0] txdata = 0;
+reg [7:0] next_txdata;
+
+wire [7:0]txshift = { 2'b0, txdata[7:2] };
+
+reg [1:0]txd = 0;
+reg [1:0]next_txd;
+
+assign { tx1, tx0 } = txd;
+
+reg [5:0] count = 0;
+reg [5:0] next_count;
+
+wire count_is_zero = (count == 0);
+
+wire [5:0]count_minus_one = (count - 1);
+
+reg next_txen;
+reg next_advance;
+reg next_busy;
+
+always_comb begin
+ next_state = state;
+ next_count = count;
+ next_txdata = txdata;
+ next_busy = busy;
+ next_txd = txd;
+ next_txen = 1;
+ next_advance = 0;
+
+ case (state)
+ IDLE: begin
+ next_txen = 0;
+ next_txd = 0;
+ if (packet) begin
+ next_state = PRE;
+ next_count = 31;
+ next_busy = 1;
+ end
+ end
+ PRE: begin
+ if (count_is_zero) begin
+ next_state = DAT0;
+ next_txdata = data;
+ next_advance = 1;
+ next_txd = 2'b11;
+ end else begin
+ next_txd = 2'b01;
+ next_count = count_minus_one;
+ end
+ end
+ DAT0: begin
+ next_state = DAT1;
+ next_txdata = txshift;
+ next_txd = txdata[1:0];
+ end
+ DAT1: begin
+ next_state = DAT2;
+ next_txdata = txshift;
+ next_txd = txdata[1:0];
+ end
+ DAT2: begin
+ next_state = DAT3;
+ next_txdata = txshift;
+ next_txd = txdata[1:0];
+ end
+ DAT3: begin
+ next_txd = txdata[1:0];
+ if (~packet) begin
+ // no more data, wrap it up
+ next_state = EOP;
+ next_count = 48;
+ end else begin
+ next_state = DAT0;
+ next_txdata = data;
+ next_advance = 1;
+ end
+ end
+ EOP: begin
+ next_txd = 0;
+ next_txen = 0;
+ if (count_is_zero) begin
+ next_state = IDLE;
+ next_busy = 0;
+ end else begin
+ next_count = count_minus_one;
+ end
+ end
+ endcase
+end
+
+always_ff @(posedge clk50) begin
+ state <= next_state;
+ count <= next_count;
+ txdata <= next_txdata;
+ txen <= next_txen;
+ txd <= next_txd;
+ advance <= next_advance;
+ busy <= next_busy;
+end
+
+endmodule
diff --git a/hdl/test/eth_crc32_test.sv b/hdl/test/eth_crc32_test.sv
@@ -0,0 +1,54 @@
+`timescale 1ns / 1ps
+
+module testbench(input clk);
+
+wire [31:0]val;
+wire [7:0]data;
+
+eth_crc32 crc(
+ .clk(clk),
+ .en(1),
+ .rst(0),
+ .dat(data),
+ .crc(val)
+ );
+
+reg [7:0]packet[0:15];
+
+reg [7:0]count = 0;
+
+assign data = packet[count[3:0]];
+
+always_ff @(posedge clk) begin
+ $display("crc %x %x",data, val);
+ if (count == 16) begin
+ if (val == 32'hdebb20e3)
+ $display("PASS");
+ else
+ $display("FAIL");
+ $finish();
+ end
+ count <= count + 1;
+end
+
+initial begin
+ packet[0] = 8'h6e;
+ packet[1] = 8'hb9;
+ packet[2] = 8'h34;
+ packet[3] = 8'h70;
+ packet[4] = 8'h3b;
+ packet[5] = 8'h77;
+ packet[6] = 8'hc7;
+ packet[7] = 8'hae;
+ packet[8] = 8'h29;
+ packet[9] = 8'h52;
+ packet[10] = 8'h14;
+ packet[11] = 8'h3e;
+ packet[12] = 8'h09;
+ packet[13] = 8'ha6;
+ packet[14] = 8'h94;
+ packet[15] = 8'h60;
+end
+
+endmodule
+
diff --git a/hdl/test/eth_rmii_test.sv b/hdl/test/eth_rmii_test.sv
@@ -0,0 +1,111 @@
+/* Copyright 2014 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+`timescale 1ns / 1ps
+
+module testbench(input clk);
+
+// RMII transport between tx and rx
+wire eth_d0;
+wire eth_d1;
+wire eth_en;
+
+
+wire [7:0]txdata;
+reg txpacket = 0;
+wire txbusy;
+wire txadvance;
+
+eth_rmii_tx tx(
+ .clk50(clk),
+
+ .tx0(eth_d0),
+ .tx1(eth_d1),
+ .txen(eth_en),
+
+ .data(txdata),
+ .packet(txpacket),
+ .busy(txbusy),
+ .advance(txadvance)
+ );
+
+wire [7:0]rxdata;
+wire rxvalid;
+wire rxeop;
+
+eth_rmii_rx rx(
+ .clk50(clk),
+ .rx0(eth_d0),
+ .rx1(eth_d1),
+ .crs_dv(eth_en),
+ .data(rxdata),
+ .valid(rxvalid),
+ .eop(rxeop)
+ );
+
+reg txgo = 0;
+reg wait_eop = 0;
+
+reg [7:0]pdata[0:15];
+reg [3:0]pindex = 0;
+
+assign txdata = pdata[pindex];
+
+reg [7:0]start = 8'b10000000;
+
+always_ff @(posedge clk) begin
+ txgo <= start[0];
+ start <= { 1'b0, start[7:1] };
+end
+
+
+always_ff @(posedge clk) begin
+ if (txgo) begin
+ txpacket <= 1;
+ end
+ if (txpacket) begin
+ if (txadvance) begin
+ if (pindex == 15) begin
+ txpacket <= 0;
+ wait_eop <= 1;
+ end
+ pindex <= pindex + 1;
+ end
+ end
+ if (wait_eop & ~txbusy) begin
+ $finish;
+ end
+end
+
+initial begin
+ pdata[0] = 8'hFF;
+ pdata[1] = 8'h01;
+ pdata[2] = 8'h77;
+ pdata[3] = 8'hAA;
+ pdata[4] = 8'h00;
+ pdata[5] = 8'h10;
+ pdata[6] = 8'h20;
+ pdata[7] = 8'h30;
+ pdata[8] = 8'h40;
+ pdata[9] = 8'h50;
+ pdata[10] = 8'h60;
+ pdata[11] = 8'h70;
+ pdata[12] = 8'h80;
+ pdata[13] = 8'h90;
+ pdata[14] = 8'hAA;
+ pdata[15] = 8'h55;
+end
+
+endmodule