diff --git a/Makefile b/Makefile index 0b9e648..d23c409 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,11 @@ auto_gen: # Functional Simulation functional_sim: io_core_tb logic_analyzer_tb bit_fifo_tb bridge_rx_tb bridge_tx_tb lut_ram_tb +mac_tb: + iverilog -g2012 -o sim.out -y src/manta test/functional_sim/mac_tb.sv + vvp sim.out + rm sim.out + block_memory_tb: iverilog -g2012 -o sim.out -y src/manta test/functional_sim/block_memory_tb.sv vvp sim.out diff --git a/examples/nexys_a7/ethernet_logic_analyzer/manta.yaml b/examples/nexys_a7/ethernet_logic_analyzer/manta.yaml deleted file mode 100644 index 370fcff..0000000 --- a/examples/nexys_a7/ethernet_logic_analyzer/manta.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -cores: - ether_la: - type: logic_analyzer - sample_depth: 17000 - trigger_loc: 50 - - probes: - eth_crsdv: 1 - eth_rxd: 2 - eth_txen: 1 - eth_txd: 2 - - triggers: - - eth_crsdv RISING - -uart: - port: "auto" - baudrate: 115200 - clock_freq: 50000000 \ No newline at end of file diff --git a/examples/nexys_a7/ethernet_logic_analyzer/send_packet.py b/examples/nexys_a7/ethernet_logic_analyzer/send_packet.py deleted file mode 100644 index 4005c8e..0000000 --- a/examples/nexys_a7/ethernet_logic_analyzer/send_packet.py +++ /dev/null @@ -1,24 +0,0 @@ -from manta import Manta -from scapy.all import * - -m = Manta('manta.yaml') - - -def set_led(val): - src_mac = "00:00:00:00:00:00" - dst_mac = "FF:FF:FF:FF:FF:FF" - ifc = "en8" - - mypkt = Ether() - mypkt.src = src_mac - mypkt.dst = dst_mac - mypkt.type = 0x1234 - - msg = b'\x56\x78' + val.to_bytes(2, 'big') - - mypkt = mypkt / msg - mypkt.load = msg - sendpfast(mypkt, iface=ifc) - -while(True): - set_led(0) diff --git a/examples/nexys_a7/ethernet_logic_analyzer/sim/mac_tx_tb.sv b/examples/nexys_a7/ethernet_logic_analyzer/sim/mac_tx_tb.sv deleted file mode 100644 index 20c705d..0000000 --- a/examples/nexys_a7/ethernet_logic_analyzer/sim/mac_tx_tb.sv +++ /dev/null @@ -1,111 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps - -module mac_tx_tb(); - logic ethclk; - logic rst; - - always begin - #5; - ethclk = !ethclk; - end - - /* batteries... */ - logic eth_crsdv; - logic[1:0] eth_rxd; - - /* ether -> { cksum, bitorder } */ - logic[1:0] ether_axiod; - logic ether_axiov; - - /* cksum -> top_level */ - logic cksum_done, cksum_kill; - - /* bitorder -> firewall */ - logic[1:0] bitorder_axiod; - logic bitorder_axiov; - - /* firewall -> aggregate */ - logic[1:0] firewall_axiod; - logic firewall_axiov; - - /* aggregate output */ - logic[31:0] aggregate_axiod; - logic aggregate_axiov; - - /* and here's the pipeline... */ - - logic eth_crsdv_mtx; - logic [1:0] eth_rxd_mtx; - - logic mtx_start; - mac_tx mtx ( - .clk(ethclk), - - .data(16'h5679), - - .start(mtx_start), - - .txen(eth_crsdv_mtx), - .txd(eth_rxd_mtx)); - - ether e( - .clk(ethclk), - .rst(rst), - .rxd(eth_rxd_mtx), - .crsdv(eth_crsdv_mtx), - .axiov(ether_axiov), - .axiod(ether_axiod)); - - bitorder b( - .clk(ethclk), - .rst(rst), - .axiiv(ether_axiov), - .axiid(ether_axiod), - .axiov(bitorder_axiov), - .axiod(bitorder_axiod)); - - firewall f( - .clk(ethclk), - .rst(rst), - .axiiv(bitorder_axiov), - .axiid(bitorder_axiod), - .axiov(firewall_axiov), - .axiod(firewall_axiod)); - - aggregate a( - .clk(ethclk), - .rst(rst), - .axiiv(firewall_axiov), - .axiid(firewall_axiod), - .axiov(aggregate_axiov), - .axiod(aggregate_axiod)); - - cksum c( - .clk(ethclk), - .rst(rst), - .axiiv(ether_axiov), - .axiid(ether_axiod), - .done(cksum_done), - .kill(cksum_kill)); - - initial begin - ethclk = 0; - $dumpfile("mac_tx_tb.vcd"); - $dumpvars(0, mac_tx_tb); - rst = 0; - mtx_start = 0; - #10; - rst = 1; - #10; - rst = 0; - #10; - mtx_start = 1; - - #10000; - - $finish(); - end - -endmodule -`default_nettype wire \ No newline at end of file diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/bto7s.sv b/examples/nexys_a7/ethernet_logic_analyzer/src/bto7s.sv deleted file mode 100644 index 8ac30a8..0000000 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/bto7s.sv +++ /dev/null @@ -1,49 +0,0 @@ -module bto7s( - input wire [3:0] x_in, - output logic [6:0] s_out); - - logic sa, sb, sc, sd, se, sf, sg; - assign s_out = {sg, sf, se, sd, sc, sb, sa}; - - // array of bits that are "one hot" with numbers 0 through 15 - logic [15:0] num; - - assign num[0] = ~x_in[3] && ~x_in[2] && ~x_in[1] && ~x_in[0]; - assign num[1] = ~x_in[3] && ~x_in[2] && ~x_in[1] && x_in[0]; - assign num[2] = x_in == 4'd2; - assign num[3] = x_in == 4'd3; - assign num[4] = x_in == 4'd4; - assign num[5] = x_in == 4'd5; - assign num[6] = x_in == 4'd6; - assign num[7] = x_in == 4'd7; - assign num[8] = x_in == 4'd8; - assign num[9] = x_in == 4'd9; - assign num[10] = x_in == 4'd10; - assign num[11] = x_in == 4'd11; - assign num[12] = x_in == 4'd12; - assign num[13] = x_in == 4'd13; - assign num[14] = x_in == 4'd14; - assign num[15] = x_in == 4'd15; - - /* you could also do this with generation, like this: - * - * genvar i; - * generate - * for (i=0; i<16; i=i+1)begin - * assign num[i] = (x_in == i); - * end - * endgenerate - */ - - /* assign the seven output segments, sa through sg, using a "sum of products" - * approach and the diagram above. - */ - - assign sa = num[0] || num[2] || num[3] || num[5] || num[6] || num[7] || num[8] || num[9] || num[10] || num[12] ||num[14] ||num[15]; - assign sb = num[0] || num[1] || num[2] || num[3] || num[4] || num[7] || num[8] || num[9] || num[10] || num[13]; - assign sc = num[0] || num[1] || num[3] || num[4] || num[5] || num[6] || num[7] || num[8] || num[9] || num[10] || num[11] || num[13]; - assign sd = num[0] || num[2] || num[3] || num[5] || num[6] || num[8] || num[9] || num[11] || num[12] || num[13] || num[14]; - assign se = num[0] || num[2] || num[6] || num[8] || num[10] || num[11] || num[12] || num[13] || num[14] || num[15]; - assign sf = num[0] || num[4] || num[5] || num[6] || num[8] || num[9] || num[10] || num[11] || num[12] || num[14] || num[15]; - assign sg = num[2] || num[3] || num[4] || num[5] || num[6] || num[8] || num[9] || num[10] || num[11] || num[13] || num[14] ||num[15]; -endmodule diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/seven_segment_controller.sv b/examples/nexys_a7/ethernet_logic_analyzer/src/seven_segment_controller.sv deleted file mode 100644 index 1231b2a..0000000 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/seven_segment_controller.sv +++ /dev/null @@ -1,45 +0,0 @@ -module seven_segment_controller #(parameter COUNT_TO = 100000) - ( input wire clk_in, - input wire rst_in, - input wire [31:0] val_in, - output logic[6:0] cat_out, - output logic[7:0] an_out - ); - - logic[7:0] segment_state; - logic[31:0] segment_counter; - logic [3:0] routed_vals; - logic [6:0] led_out; - - bto7s mbto7s (.x_in(routed_vals), .s_out(led_out)); - - assign cat_out = ~led_out; - assign an_out = ~segment_state; - - always_comb begin - case(segment_state) - 8'b0000_0001: routed_vals = val_in[3:0]; - 8'b0000_0010: routed_vals = val_in[7:4]; - 8'b0000_0100: routed_vals = val_in[11:8]; - 8'b0000_1000: routed_vals = val_in[15:12]; - 8'b0001_0000: routed_vals = val_in[19:16]; - 8'b0010_0000: routed_vals = val_in[23:20]; - 8'b0100_0000: routed_vals = val_in[27:24]; - 8'b1000_0000: routed_vals = val_in[31:28]; - default: routed_vals = val_in[3:0]; - endcase - end - always_ff @(posedge clk_in)begin - if (rst_in)begin - segment_state <= 8'b0000_0001; - segment_counter <= 32'b0; - end else begin - if (segment_counter == COUNT_TO)begin - segment_counter <= 32'd0; - segment_state <= {segment_state[6:0],segment_state[7]}; - end else begin - segment_counter <= segment_counter +1; - end - end - end -endmodule //seven_segment_controller diff --git a/examples/nexys_a7/ethernet_logic_analyzer/lab-bc.py b/examples/nexys_a7/lut_ram_ether/lab-bc.py similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/lab-bc.py rename to examples/nexys_a7/lut_ram_ether/lab-bc.py diff --git a/examples/nexys_a7/lut_ram_ether/manta.yaml b/examples/nexys_a7/lut_ram_ether/manta.yaml new file mode 100644 index 0000000..45bdb31 --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/manta.yaml @@ -0,0 +1,10 @@ +--- +cores: + my_lut_ram: + type: lut_ram + size: 64 + +uart: + port: "auto" + baudrate: 115200 + clock_freq: 100000000 \ No newline at end of file diff --git a/examples/nexys_a7/lut_ram_ether/read_write_test.py b/examples/nexys_a7/lut_ram_ether/read_write_test.py new file mode 100644 index 0000000..2615797 --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/read_write_test.py @@ -0,0 +1,12 @@ +from manta import Manta +from random import randint + +m = Manta('manta.yaml') + +for addr in range(m.my_lut_ram.size): + write_data = randint(0, (2**16)-1) + m.my_lut_ram.write(addr, write_data) + + read_data = m.my_lut_ram.read(addr) + print(f"test addr: {addr} with data: {write_data}") + print(f" -> correct data received on readback?: {write_data == read_data}") \ No newline at end of file diff --git a/examples/nexys_a7/lut_ram_ether/send_packet.py b/examples/nexys_a7/lut_ram_ether/send_packet.py new file mode 100644 index 0000000..243d4ff --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/send_packet.py @@ -0,0 +1,20 @@ +from manta import Manta +from scapy.all import * + +m = Manta('manta.yaml') + +src_mac = "00:00:00:00:00:00" +dst_mac = "FF:FF:FF:FF:FF:FF" +ifc = "en8" + +mypkt = Ether() +mypkt.src = src_mac +mypkt.dst = dst_mac +mypkt.type = 0x0002 + +msg = b'\x00\x00' + +mypkt = mypkt / msg +mypkt.load = msg +p = srp(mypkt, iface=ifc) +p.show() diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/aggregate.sv b/examples/nexys_a7/lut_ram_ether/src/aggregate.v similarity index 94% rename from examples/nexys_a7/ethernet_logic_analyzer/src/aggregate.sv rename to examples/nexys_a7/lut_ram_ether/src/aggregate.v index d2d8f06..e2e7772 100644 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/aggregate.sv +++ b/examples/nexys_a7/lut_ram_ether/src/aggregate.v @@ -13,7 +13,7 @@ * of your module! */ -`define AGR_MAX 32 +`define AGR_MAX 48 `define AGR_SHOW 64 module aggregate(clk, rst, axiid, axiiv, axiod, axiov); @@ -36,9 +36,9 @@ module aggregate(clk, rst, axiid, axiiv, axiod, axiov); * just the first 32 bits of the incoming transmission, * asserted for a single cycle */ - output logic[31:0] axiod; + output logic[47:0] axiod; output logic axiov; - + /* A quick and dirty counter. As long as this is below * 32, we'll dump packets into the AXI output data buffer. * Once the counter gets to AGR_MAX, we'll assert AXI valid. @@ -56,7 +56,7 @@ module aggregate(clk, rst, axiid, axiiv, axiod, axiov); always_ff @(posedge clk) begin: AXIOD if (rst || !axiiv) axiod <= 32'b0; else if (counter < `AGR_MAX && axiiv) - axiod[`AGR_MAX - counter - 2 +: 2] = axiid; + axiod[`AGR_MAX - counter - 2 +: 2] <= axiid; end endmodule diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/bitorder.sv b/examples/nexys_a7/lut_ram_ether/src/bitorder.v similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/src/bitorder.sv rename to examples/nexys_a7/lut_ram_ether/src/bitorder.v diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/cksum.sv b/examples/nexys_a7/lut_ram_ether/src/cksum.v similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/src/cksum.sv rename to examples/nexys_a7/lut_ram_ether/src/cksum.v diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/crc32.sv b/examples/nexys_a7/lut_ram_ether/src/crc32.v similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/src/crc32.sv rename to examples/nexys_a7/lut_ram_ether/src/crc32.v diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/divider.sv b/examples/nexys_a7/lut_ram_ether/src/divider.sv similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/src/divider.sv rename to examples/nexys_a7/lut_ram_ether/src/divider.sv diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/ether.sv b/examples/nexys_a7/lut_ram_ether/src/ether.v similarity index 86% rename from examples/nexys_a7/ethernet_logic_analyzer/src/ether.sv rename to examples/nexys_a7/lut_ram_ether/src/ether.v index acc2abe..9e84886 100644 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/ether.sv +++ b/examples/nexys_a7/lut_ram_ether/src/ether.v @@ -1,15 +1,3 @@ -/* The catsoop checker does not like to see `timescale declarations - * in your code. However, we obviously need them when we synthesize - * for iverilog - they tell iverilog that #5 means 5ns, for example. - * Without these iverilog has no basis for how long *stuff* should - * wait for when you use #, so it blows up! - * - * When you use the catsoop checker, it `defines the CATSOOP macro. - * So we can check whether CATSOOP is defined or not - if it isn't, - * then we'll put the timescale thing in so code works right on your - * system. - */ - `default_nettype none `timescale 1ns / 1ps @@ -37,7 +25,7 @@ `define EF_IDLE 3'b000 `define EF_PREAM 3'b001 `define EF_DATA 3'b011 -`define EF_BAD 3'b101 +`define EF_BAD 3'b101 `define PREAM_BITS 64 `define PREAM_SIZE (`PREAM_BITS / 2) @@ -68,7 +56,7 @@ module ether(clk, rst, rxd, crsdv, axiov, axiod); * So just use this input to determine whether valid * data is on the line coming in. */ - input logic crsdv; + input logic crsdv; /* Receive Data (RXD): If crsdv is high, receives * two bits off the wire. Otherwise, undefined @@ -108,7 +96,7 @@ module ether(clk, rst, rxd, crsdv, axiov, axiod); preamok = crsdv && rxd == preamex; end - + always @(*) start = crsdv && rxd != `PREAM_FIRST; always @(posedge clk) begin: COUNT diff --git a/examples/nexys_a7/lut_ram_ether/src/ethernet_rx.v b/examples/nexys_a7/lut_ram_ether/src/ethernet_rx.v new file mode 100644 index 0000000..aab553e --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/src/ethernet_rx.v @@ -0,0 +1,40 @@ +`default_nettype none +`timescale 1ns/1ps + +module ethernet_rx ( + input wire clk, + + input wire crsdv, + input wire [1:0] rxd, + + output reg [15:0] addr_o, + output reg [15:0] wdata_o, + output reg rw_o, + output reg valid_o + ); + + // we know if the packet is a read or write + // based on the ethertype. + + reg [15:0] ethertype; + reg [31:0] data; + reg valid; + + mac_rx mrx ( + .clk(clk), + + .crsdv(crsdv), + .rxd(rxd), + + .ethertype(ethertype), + .data(data), + .valid(valid)); + + assign addr_o = data[31:16]; + assign wdata_o = data[15:0]; + assign rw_o = (ethertype == 4); + assign valid_o = valid; + +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/examples/nexys_a7/lut_ram_ether/src/ethernet_tx.v b/examples/nexys_a7/lut_ram_ether/src/ethernet_tx.v new file mode 100644 index 0000000..3003886 --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/src/ethernet_tx.v @@ -0,0 +1,27 @@ +`default_nettype none +`timescale 1ns/1ps + +module ethernet_tx( + input wire clk, + + output reg txen, + output reg [1:0] txd, + + input wire [15:0] rdata_i, + input wire rw_i, + input wire valid_i + ); + + mac_tx mtx ( + .clk(clk), + + .data(rdata_i), + .ethertype(16'h2), + .start(~rw_i && valid_i), + + .txen(txen), + .txd(txd)); + +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/firewall.sv b/examples/nexys_a7/lut_ram_ether/src/firewall.v similarity index 99% rename from examples/nexys_a7/ethernet_logic_analyzer/src/firewall.sv rename to examples/nexys_a7/lut_ram_ether/src/firewall.v index a0b946c..af4c2a3 100644 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/firewall.sv +++ b/examples/nexys_a7/lut_ram_ether/src/firewall.v @@ -32,7 +32,7 @@ `define FW_DESTSTART 0 `define FW_DESTEND (`FW_DESTSTART + 48) -`define FW_DATASTART (48 + 48 + 16) +`define FW_DATASTART (48 + 48) module firewall(clk, rst, axiiv, axiid, axiov, axiod); diff --git a/examples/nexys_a7/lut_ram_ether/src/mac_rx.v b/examples/nexys_a7/lut_ram_ether/src/mac_rx.v new file mode 100644 index 0000000..843dbee --- /dev/null +++ b/examples/nexys_a7/lut_ram_ether/src/mac_rx.v @@ -0,0 +1,116 @@ +`default_nettype none +`timescale 1ns/1ps + +module mac_rx ( + input wire clk, + + input wire crsdv, + input wire [1:0] rxd, + + output reg [15:0] ethertype, + output reg [31:0] data, + output reg valid); + + // TODO: rewrite modules to not need external reset + reg rst = 1; + always @(posedge clk) rst <= 0; + + /* ether -> { cksum, bitorder } */ + reg[1:0] ether_axiod; + reg ether_axiov; + + ether e( + .clk(clk), + .rst(rst), + .rxd(rxd), + .crsdv(crsdv), + .axiov(ether_axiov), + .axiod(ether_axiod)); + + /* bitorder -> firewall */ + reg[1:0] bitorder_axiod; + reg bitorder_axiov; + + bitorder b( + .clk(clk), + .rst(rst), + .axiiv(ether_axiov), + .axiid(ether_axiod), + .axiov(bitorder_axiov), + .axiod(bitorder_axiod)); + + /* firewall -> aggregate */ + reg[1:0] firewall_axiod; + reg firewall_axiov; + + firewall f( + .clk(clk), + .rst(rst), + .axiiv(bitorder_axiov), + .axiid(bitorder_axiod), + .axiov(firewall_axiov), + .axiod(firewall_axiod)); + + /* aggregate output */ + reg[47:0] aggregate_axiod; + reg aggregate_axiov; + + aggregate a( + .clk(clk), + .rst(rst), + .axiiv(firewall_axiov), + .axiid(firewall_axiod), + .axiov(aggregate_axiov), + .axiod(aggregate_axiod)); + + /* cksum -> top_level */ + reg cksum_done; + reg cksum_kill; + + cksum c( + .clk(clk), + .rst(rst), + .axiiv(ether_axiov), + .axiid(ether_axiod), + .done(cksum_done), + .kill(cksum_kill)); + + // state machine + localparam IDLE = 0; + localparam WAIT_FOR_DATA = 1; + localparam WAIT_FOR_FCS = 2; + + reg [1:0] state = IDLE; + + initial valid = 0; + initial data = 0; + + always @(posedge clk) begin + valid <= 0; + + if(state == IDLE) begin + if(crsdv) state <= WAIT_FOR_DATA; + end + + else if(state == WAIT_FOR_DATA) begin + if(aggregate_axiov) begin + state <= WAIT_FOR_FCS; + ethertype <= aggregate_axiod[47:32]; + data <= aggregate_axiod[31:0]; + + end + + // if aggregate never gives us data, go back to idle when the packet ends + else if(cksum_done) state <= IDLE; + end + + else if(state == WAIT_FOR_FCS) begin + if(cksum_done) begin + state <= IDLE; + valid <= ~cksum_kill; + end + end + end +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/mac_tx.v b/examples/nexys_a7/lut_ram_ether/src/mac_tx.v similarity index 98% rename from examples/nexys_a7/ethernet_logic_analyzer/src/mac_tx.v rename to examples/nexys_a7/lut_ram_ether/src/mac_tx.v index 3bd2fbd..f7b39f0 100644 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/mac_tx.v +++ b/examples/nexys_a7/lut_ram_ether/src/mac_tx.v @@ -6,6 +6,7 @@ module mac_tx ( // TODO: make this variable width input wire [15:0] data, + input wire [15:0] ethertype, input wire start, output reg txen, @@ -16,7 +17,6 @@ module mac_tx ( localparam SFD = 8'b11010101; parameter SRC_MAC = 48'h69_69_69_69_69_69; parameter DST_MAC = 48'hFF_FF_FF_FF_FF_FF; - parameter ETHERTYPE = 16'h1234; // all lengths are in units of dibits, hence all the mulitplies by four localparam PREAMBLE_LEN = 7 * 4; @@ -202,7 +202,7 @@ module mac_tx ( ETHERTYPE_STATE: begin bitorder_axiiv = 1; - bitorder_axiid = ETHERTYPE[2*(ETHERTYPE_LEN-counter)-1-:2]; + bitorder_axiid = ethertype[2*(ETHERTYPE_LEN-counter)-1-:2]; txen = bitorder_axiov; txd = bitorder_axiod; end diff --git a/examples/nexys_a7/ethernet_logic_analyzer/src/top_level.sv b/examples/nexys_a7/lut_ram_ether/src/top_level.sv similarity index 50% rename from examples/nexys_a7/ethernet_logic_analyzer/src/top_level.sv rename to examples/nexys_a7/lut_ram_ether/src/top_level.sv index 85461ce..d7583b0 100644 --- a/examples/nexys_a7/ethernet_logic_analyzer/src/top_level.sv +++ b/examples/nexys_a7/lut_ram_ether/src/top_level.sv @@ -30,40 +30,12 @@ module top_level ( manta manta_inst ( .clk(clk_50mhz), - .rx(uart_txd_in), - .tx(uart_rxd_out), - - .eth_crsdv(eth_crsdv), - .eth_rxd(eth_rxd), - .eth_txen(eth_txen), - .eth_txd(eth_txd)); - - // packet_blaster_9k pb9k ( - // .clk(clk_50mhz), - // .rst(btnc), - - // //.src_mac(48'h69_2C_08_30_75_FD), - // .src_mac(48'b00_00_00_00_00_00), - // .dst_mac(48'hFF_FF_FF_FF_FF_FF), - - // .data(16'h5678), - - // .start(btnd), - - // .txen(eth_txen), - // .txd(eth_txd)); - - - mac_tx mtx ( - .clk(clk_50mhz), - - .data(sw), - - .start(btnd), - + .crsdv(eth_crsdv), + .rxd(eth_rxd), .txen(eth_txen), .txd(eth_txd)); + endmodule `default_nettype wire \ No newline at end of file diff --git a/examples/nexys_a7/ethernet_logic_analyzer/xdc/top_level.xdc b/examples/nexys_a7/lut_ram_ether/xdc/top_level.xdc similarity index 100% rename from examples/nexys_a7/ethernet_logic_analyzer/xdc/top_level.xdc rename to examples/nexys_a7/lut_ram_ether/xdc/top_level.xdc diff --git a/src/manta/aggregate.v b/src/manta/aggregate.v new file mode 100644 index 0000000..e2e7772 --- /dev/null +++ b/src/manta/aggregate.v @@ -0,0 +1,64 @@ +`default_nettype none +`timescale 1ns / 1ps + +/* Aggregates the first 64 bits of an incoming + * Ethernet transmission (thus shedding the FCS + * and anything else extraneous) and outputs the + * first 32 bits on an AXI bus for a single cycle. + * If the packet is not at least 64 bits long, + * nothing happens + * + * This value can then be fed into the seven + * segment display to verify proper operation + * of your module! + */ + +`define AGR_MAX 48 +`define AGR_SHOW 64 + +module aggregate(clk, rst, axiid, axiiv, axiod, axiov); + + /* batteries */ + input logic clk, rst; + + /* AXI input valid, and AXI input data from the + * Ethernet pipeline. Comprises only data, i.e. + * source/destination/ethertype are omitted via + * previous stages in the pipeline. Also technically + * comprises the FCS, but we assume that the actual + * data in the ethernet frame is >= 32 bits long + * so we'll lose the FCS in this stage by design + */ + input logic[1:0] axiid; + input logic axiiv; + + /* AXI output valid, and AXI output data. Comprises + * just the first 32 bits of the incoming transmission, + * asserted for a single cycle + */ + output logic[47:0] axiod; + output logic axiov; + + /* A quick and dirty counter. As long as this is below + * 32, we'll dump packets into the AXI output data buffer. + * Once the counter gets to AGR_MAX, we'll assert AXI valid. + * Then we'll hang until axiiv drops + */ + logic[31:0] counter; + + assign axiov = counter == `AGR_SHOW; + + always_ff @(posedge clk) begin: COUNTER + if (rst || !axiiv) counter <= 32'b0; + else counter <= counter + 2; + end + + always_ff @(posedge clk) begin: AXIOD + if (rst || !axiiv) axiod <= 32'b0; + else if (counter < `AGR_MAX && axiiv) + axiod[`AGR_MAX - counter - 2 +: 2] <= axiid; + end + +endmodule + +`default_nettype wire diff --git a/src/manta/bitorder.v b/src/manta/bitorder.v new file mode 100644 index 0000000..0de0c15 --- /dev/null +++ b/src/manta/bitorder.v @@ -0,0 +1,138 @@ +`default_nettype none +`timescale 1ns / 1ps + +/* Ethernet sends packets in least significant + * bit order, most significant byte order. This + * module is responsible for changing that to + * make things legible - in particular, we convert + * from LSb/MSB to MSb/MSB. + */ + +`define BO_SENDA 2'b00 +`define BO_SENDB 2'b01 +`define BO_EMPTYA 2'b10 +`define BO_EMPTYB 2'b11 + +module bitorder(clk, rst, axiiv, axiid, axiod, axiov); + + /* batteries */ + input logic clk, rst; + + /* AXI input valid and AXI input data + * from the module upstream from us in the + * pipeline - that being the physical layer + * This will transport bits from off the wire + * in least significant bit first order + */ + input logic[1:0] axiid; + input logic axiiv; + + /* AXI output valid and AXI output data + * Transports the correctly bit-ordered Ethernet + * data (most significant bit first) up the pipeline + * for further processing... + */ + output logic[1:0] axiod; + output logic axiov; + + /* Two registers to hold data coming in off the wire, + * byte by byte. This is where we'll buffer until + * we've received a byte of data, at which point + * we'll start sending out the byte in the correct + * order using one register. Meanwhile, we'll start + * receiving into the other register - dual buffers. + */ + logic[7:0] bufa, bufb; + + /* A counter. This indicates what 'stage' we're in, + * and always refers to the index we're reading into + * in the receiving buffer or sending out of in the + * sending buffer + */ + logic[2:0] countera, counterb; + + /* Which state we're in - should we be using buffer + * A to send, buffer B to send, or neither because + * we've just come out of reset? + */ + logic[1:0] state; + + always_comb begin: AXIOV + if (state == `BO_SENDA || state == `BO_SENDB) axiov = 1'b1; + else axiov = 1'b0; + end + + always_comb begin: AXIOD + if (state == `BO_SENDA) axiod = bufa[countera +: 2]; + else if (state == `BO_SENDB) axiod = bufb[counterb +: 2]; + else axiod = 1'b0; + end + + always_ff @(posedge clk) begin: BUFFERIN + if (rst) begin + bufa <= 8'b0; + bufb <= 8'b0; + end else if (axiiv) begin + case (state) + `BO_EMPTYB, `BO_SENDB: + bufa[countera +: 2] <= axiid; + `BO_EMPTYA, `BO_SENDA: + bufb[counterb +: 2] <= axiid; + endcase + end else if (state == `BO_EMPTYB || state == `BO_EMPTYA) begin + bufa <= 8'b0; + bufb <= 8'b0; + end + end + + always_ff @(posedge clk) begin: STATES + if (rst) begin + state <= `BO_EMPTYB; + countera <= 3'b0; + counterb <= 3'b0; + end else begin + case (state) + `BO_EMPTYB: begin + if (axiiv) begin + if (countera == 3'h6) + state <= `BO_SENDA; + else countera <= countera + 2; + end else countera <= 3'b0; + end + + `BO_EMPTYA: begin + if (axiiv) begin + if (counterb == 3'h6) + state <= `BO_SENDB; + else counterb <= counterb + 2; + end else counterb <= 3'b0; + end + + `BO_SENDB: begin + if (counterb == 3'h0) state <= `BO_EMPTYB; + else counterb <= counterb - 2; + + if (axiiv) begin + if (countera == 3'h6) + state <= `BO_SENDA; + else countera <= countera + 2; + end + end + + `BO_SENDA: begin + if (countera == 3'h0) state <= `BO_EMPTYA; + else countera <= countera - 2; + + if (axiiv) begin + if (counterb == 3'h6) + state <= `BO_SENDB; + else counterb <= counterb + 2; + end + end + endcase + end + end + +endmodule + +`default_nettype wire diff --git a/src/manta/cksum.v b/src/manta/cksum.v new file mode 100644 index 0000000..59dcfaa --- /dev/null +++ b/src/manta/cksum.v @@ -0,0 +1,95 @@ +`default_nettype none +`timescale 1ns / 1ps + +/* Computes the ethernet checksum + * The following combinations of `done` and `kill` + * represent the state of the module: + * + * - done = 0, kill = 0: processing data or freshly reset + * - done = 1, kill = 0: correct ethernet checksum verified + * - done = 1, kill = 1: data valid set to zero before correct + * checksum value computed, therefore bad checksum + * - done = 0, kill = 1: never asserted + * + * the done and/or kill signals are asserted high beginning + * the cycle after input data ceases, and until new data + * is received via the AXI input + */ + +`define CK_FRESH 2'b00 +`define CK_COMPUTING 2'b01 +`define CK_DONE 2'b10 + +`define MAGIC_CHECK 32'h38_fb_22_84 + +module cksum(clk, rst, axiid, axiiv, done, kill); + + /* batteries */ + input logic clk, rst; + + /* AXI input valid, and AXI input data from the + * physical layer. Comprises unmodified data directly + * from the wire, in Ethernet bit order, to be fed + * directly into the CRC32 module you wrote for the + * pset + */ + input logic[1:0] axiid; + input logic axiiv; + + /* Done and kill, as described in the module synopsis */ + output logic done, kill; + + /* CRC32 AXI output data bus, which is the 32-bit + * checksum calculated so far via the checksum module + * you implemented in one of the last psets (CRC32-BZIP2) + */ + logic[31:0] crcd; + logic crcv; + + /* Decoupled logic to reset the CRC module independently + * Used to compute multiple CRCs back to back + */ + logic crcrst; + + /* Our finite state machine - bonus points if you can identify + * whether this is a Moore or Mealy FSM! + */ + logic[1:0] state; + + crc32 cksum(.clk(clk), + .rst(crcrst | rst), + .axiiv(axiiv), + .axiid(axiid), + .axiov(crcv), + .axiod(crcd)); + + always_ff @(posedge clk) begin: OUTPUTS + if (rst || axiiv) begin + done <= 1'b0; + kill <= 1'b0; + crcrst <= 1'b0; + end else begin + if (state == `CK_COMPUTING && !axiiv) begin + done <= 1'b1; + crcrst <= 1'b1; + + if (crcd == `MAGIC_CHECK) kill <= 1'b0; + else kill <= 1'b1; + end else crcrst <= 1'b0; + end + end + + always_ff @(posedge clk) begin: FSM + if (rst) state <= `CK_FRESH; + else begin + case (state) + `CK_FRESH: if (axiiv) state <= `CK_COMPUTING; + `CK_COMPUTING: if (!axiiv) state <= `CK_DONE; + `CK_DONE: if (axiiv) state <= `CK_COMPUTING; + endcase + end + end + +endmodule + +`default_nettype wire diff --git a/src/manta/crc32.v b/src/manta/crc32.v new file mode 100644 index 0000000..a647b47 --- /dev/null +++ b/src/manta/crc32.v @@ -0,0 +1,74 @@ +`default_nettype none +`timescale 1ns / 1ps + +`define LAGGING_SHIFT_IN (caxiod[30] ^ axiid[1]) +`define LEADING_SHIFT_IN (caxiod[31] ^ axiid[0]) +`define DOUBLED_SHIFT_IN (`LEADING_SHIFT_IN ^ `LAGGING_SHIFT_IN) + +`define LAGGING_TAPS 4, 7, 10, 16, 22, 26 +`define DOUBLED_TAPS 2, 5, 8, 11, 12, 23 +`define LEADING_TAPS 3, 6, 9, 13, 17, 24, 27 + +/* this module implements CRC32-BZIP2, with a two bit input: + * - poly 0x04C11DB7 + * - init 0xFFFFFFFF + * - NEW: XOR outputs + * + * == check: 0xfc891918 == + * + * this is the ethernet checksum!! + */ + +module crc32(clk, rst, axiiv, axiid, axiov, axiod); + + /* old style i/o declaration, for clarity. + * easier on 80-char line limits... + * use this if you want, we don't care + */ + input logic clk, rst; + + input logic axiiv; + input logic[1:0] axiid; + + output logic axiov; + output logic[31:0] axiod; + + logic[31:0] caxiod, saxiod; + integer i; + + assign axiov = 1; + assign axiod = ~caxiod; + + always @(*) begin + for (i = 0; i < 32; i = i + 1) begin + case (i) + 0: saxiod[i] = `LAGGING_SHIFT_IN; + 1: saxiod[i] = `DOUBLED_SHIFT_IN; + + `LAGGING_TAPS: + saxiod[i] = caxiod[i - 2] ^ `LAGGING_SHIFT_IN; + `DOUBLED_TAPS: + saxiod[i] = caxiod[i - 2] ^ `DOUBLED_SHIFT_IN; + `LEADING_TAPS: + saxiod[i] = caxiod[i - 2] ^ `LEADING_SHIFT_IN; + + default: saxiod[i] = caxiod[i - 2]; + endcase + end + end + + always @(posedge clk) begin + if (rst) caxiod <= 32'hFFFF_FFFF; + + /* our output validity hinges on whether + * we are calculating anything or not + * on this clock cycle. if there is no + * valid input for us, don't do a shift + * this cycle + */ + else caxiod <= (axiiv) ? saxiod : caxiod; + end + +endmodule + +`default_nettype wire diff --git a/src/manta/ether.v b/src/manta/ether.v new file mode 100644 index 0000000..9e84886 --- /dev/null +++ b/src/manta/ether.v @@ -0,0 +1,139 @@ +`default_nettype none +`timescale 1ns / 1ps + +/* This module receives packets from the RJ45 (Ethernet) port on your + * FPGA from the _physical layer_, i.e. the electronic (or optical!) + * connection between devices running through an Ethernet cable. + * + * Ethernet is very diverse set of specifications, with all sorts of + * different physical layers (which we abbreviate 'the medium' or + * 'media' in plural). Ethernet media have transfer speeds ranging + * from 1 Mbps to 400 gigabits per second in modern data centers! + * For this implementation, we will implement "Fast Ethernet", which + * (not fast by today's standards) utilizes a physical layer capable + * of 100 Mbps communication. Most modern cables are backwards + * compatible with this standard. + */ + +/* Note: this file uses tabs instead of spaces for indentation. + * More modern editors should pick this up automatically, but if yours + * doesn't that might explain trouble getting things to line up. You + * can probably configure this (e.g. in vim, type ":set noexpandtab" + * in normal mode and then type ":set tabstop=8") + */ + +`define EF_IDLE 3'b000 +`define EF_PREAM 3'b001 +`define EF_DATA 3'b011 +`define EF_BAD 3'b101 + +`define PREAM_BITS 64 +`define PREAM_SIZE (`PREAM_BITS / 2) + +`define PREAM_FIRST 2'b00 +`define PREAM_EXPECT 2'b01 +`define PREAM_LAST 2'b11 +`define PREAM_BAD 2'b10 + +module ether(clk, rst, rxd, crsdv, axiov, axiod); + + input logic clk, rst; + + /* Carrier Sense (CRS) / Data Valid (DV): indicates + * whether there is a valid signal currently being + * transmitted over the Ethernet medium by another + * sender. + * + * In the past, >2 computers were connected + * to the same Ethernet cable, so this helped + * distinguish if one machine was trying to talk over + * another. Nowadays, usually Ethernet connections are + * point to point (have you ever seen an Ethernet cable + * with three ports on it? no? that's what i thought), + * and shared media isn't even supported in the 100 Mbps + * spec. + * + * So just use this input to determine whether valid + * data is on the line coming in. + */ + input logic crsdv; + + /* Receive Data (RXD): If crsdv is high, receives + * two bits off the wire. Otherwise, undefined + * (let's say 2'bXX) + * + * According to the IEEE 802.3 Fast Ethernet + * specification, with the exception of the FCS, + * bytes in Ethernet world are sent least significant + * bit first, most significant byte first. Confusing! + * Basically, if you have a two byte (16 bit) message, + * the bits will come in over RXD as: + * + * 7:6 -> 5:4 -> 3:2 -> 1:0 -> 15:14 -> ... -> 9:8 + * + * For now, this idiosyncracy won't matter to you. + * Later, it will. + */ + input logic[1:0] rxd; + + /* 2-bit AXI output: forward whatever we receive + * on to the outside world for further processing + */ + output logic axiov; + output logic[1:0] axiod; + + /* END OF STARTER CODE */ + + logic[4:0] count; + logic[2:0] state; + + logic[1:0] preamex; + logic preamok, start; + + always @(*) begin: PREAM + if (count == `PREAM_SIZE - 1) preamex = `PREAM_LAST; + else preamex = `PREAM_EXPECT; + + preamok = crsdv && rxd == preamex; + end + + always @(*) start = crsdv && rxd != `PREAM_FIRST; + + always @(posedge clk) begin: COUNT + if (state == `EF_PREAM) count <= count + 1; + else if (state == `EF_IDLE && start) count <= count + 1; + else count <= 0; + end + + always @(posedge clk) begin: FSM + if (rst) begin + axiod <= 2'b0; + axiov <= 1'b0; + state <= 3'b0; + end else begin + case (state) + `EF_BAD: if (!crsdv) state <= `EF_IDLE; + `EF_IDLE: if (start) state <= `EF_PREAM; + + `EF_PREAM: begin + if (!preamok || !crsdv) state <= `EF_BAD; + else if (count == `PREAM_SIZE - 1) + state <= `EF_DATA; + end + + `EF_DATA: begin + if (crsdv) begin + axiov <= 1'b1; + axiod <= rxd; + end else begin + axiov <= 1'b0; + axiod <= 2'b0; + state <= `EF_IDLE; + end + end + endcase + end + end +endmodule + +`default_nettype wire diff --git a/src/manta/ethernet_rx.v b/src/manta/ethernet_rx.v new file mode 100644 index 0000000..aab553e --- /dev/null +++ b/src/manta/ethernet_rx.v @@ -0,0 +1,40 @@ +`default_nettype none +`timescale 1ns/1ps + +module ethernet_rx ( + input wire clk, + + input wire crsdv, + input wire [1:0] rxd, + + output reg [15:0] addr_o, + output reg [15:0] wdata_o, + output reg rw_o, + output reg valid_o + ); + + // we know if the packet is a read or write + // based on the ethertype. + + reg [15:0] ethertype; + reg [31:0] data; + reg valid; + + mac_rx mrx ( + .clk(clk), + + .crsdv(crsdv), + .rxd(rxd), + + .ethertype(ethertype), + .data(data), + .valid(valid)); + + assign addr_o = data[31:16]; + assign wdata_o = data[15:0]; + assign rw_o = (ethertype == 4); + assign valid_o = valid; + +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/src/manta/ethernet_tx.v b/src/manta/ethernet_tx.v new file mode 100644 index 0000000..3003886 --- /dev/null +++ b/src/manta/ethernet_tx.v @@ -0,0 +1,27 @@ +`default_nettype none +`timescale 1ns/1ps + +module ethernet_tx( + input wire clk, + + output reg txen, + output reg [1:0] txd, + + input wire [15:0] rdata_i, + input wire rw_i, + input wire valid_i + ); + + mac_tx mtx ( + .clk(clk), + + .data(rdata_i), + .ethertype(16'h2), + .start(~rw_i && valid_i), + + .txen(txen), + .txd(txd)); + +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/src/manta/firewall.v b/src/manta/firewall.v new file mode 100644 index 0000000..af4c2a3 --- /dev/null +++ b/src/manta/firewall.v @@ -0,0 +1,121 @@ +`default_nettype none +`timescale 1ns / 1ps + +/* Sometimes we might receive packets not + * intended for us on the wire. This is especially + * true if we're operating in old school mode, where + * Ethernet used to have many devices on the same + * medium. Alternatively, several virtual NICs may + * exist in software on a given machine, or a machine + * may support MAC address randomization. + * + * All of this means we need to make sure inbound + * packets are actually intended for us, and not + * for some other NIC. That's what this module is + * for. In addition, this module will also strip + * the MAC address pair (and Ethertype) off of the + * Ethernet header, leaving only data and the FCS. + * We'll clean up the FCS later... + */ + +/* "Intended for us" means the following: + * - has the destination MAC "`FW_ME" as defined below. Make this + * your own MAC of your choice - get creative! + * - has the destination MAC of all 1s, i.e. a 'broadcast' packet + * + * If these conditions are not met on a given input stream, data + * from the packet is dropped / not forwarded on through the + * pipeline + */ + +`define FW_ME 48'h69_69_5A_06_54_91 +`define FW_DESTSTART 0 +`define FW_DESTEND (`FW_DESTSTART + 48) + +`define FW_DATASTART (48 + 48) + +module firewall(clk, rst, axiiv, axiid, axiov, axiod); + + /* batteries */ + input logic clk, rst; + + /* AXI input valid, and AXI input data from the bit order + * flip module. So this will transport MSb/MSB first data + * coming off the wire - allowing you to compare with src/dst + * MAC addresses a bit more easily + */ + input logic[1:0] axiid; + input logic axiiv; + + /* AXI output valid, and AXI output data. If and only if + * the packet is intended for our device as described above, + * this line will stream out the _data_ and _fcs_ (NOT the MAC + * addresses in the header, nor the ethertype - we're ignoring + * the latter this time around) we're receiving off the wire. + * If a kill-worthy condition is detected, these lines are + * deasserted for the duration of the incoming packet + */ + output logic[1:0] axiod; + output logic axiov; + + /* Buffers to hold our MAC address in the reverse order, + * to make comparison easier than it otherwise would be + */ + logic[0:47] me; + + /* A counter, to determine whether we should be comparing + * with a MAC address or stripping off data + */ + logic[31:0] counter; + + /* An internal set of flags to mark whether the currently + * traversing packet is valid, i.e we should forward data, + * or not. One of these flags tracks whether the destination + * MAC address matches _our_ (FW_ME) mac address, the other + * tracks whether the destination matches the broadcast + * (FW_BCAST) MAC. If either one of these is high once the + * destination MAC finishes rolling through, the packet + * is forwarded. + */ + logic matchme, matchbcast; + + assign me = `FW_ME; + + always_ff @(posedge clk) begin: MATCH + if (counter == 32'b0) begin + matchme <= 1'b1; + matchbcast <= 1'b1; + end + + /* could overwrite the above, which is ideal if + * FW_DESTSTART == 0 (it is) and we have a mismatch + * out the gate + */ + if (counter >= `FW_DESTSTART && counter < `FW_DESTEND) begin + if (axiiv) begin + if (axiid != {me[counter], me[counter + 1]}) + matchme <= 1'b0; + if (axiid != 2'b11) + matchbcast <= 1'b0; + end + end + end + + always_comb begin: AXIOUT + if (counter >= `FW_DATASTART && (matchme | matchbcast)) begin + axiod = axiid; + axiov = axiiv; + end else begin + axiod = 2'b00; + axiov = 1'b0; + end + end + + always_ff @(posedge clk) begin: COUNTER + if (axiiv) counter <= counter + 2; + else counter <= 32'b0; + end + +endmodule + +`default_nettype wire diff --git a/src/manta/mac_rx.v b/src/manta/mac_rx.v new file mode 100644 index 0000000..843dbee --- /dev/null +++ b/src/manta/mac_rx.v @@ -0,0 +1,116 @@ +`default_nettype none +`timescale 1ns/1ps + +module mac_rx ( + input wire clk, + + input wire crsdv, + input wire [1:0] rxd, + + output reg [15:0] ethertype, + output reg [31:0] data, + output reg valid); + + // TODO: rewrite modules to not need external reset + reg rst = 1; + always @(posedge clk) rst <= 0; + + /* ether -> { cksum, bitorder } */ + reg[1:0] ether_axiod; + reg ether_axiov; + + ether e( + .clk(clk), + .rst(rst), + .rxd(rxd), + .crsdv(crsdv), + .axiov(ether_axiov), + .axiod(ether_axiod)); + + /* bitorder -> firewall */ + reg[1:0] bitorder_axiod; + reg bitorder_axiov; + + bitorder b( + .clk(clk), + .rst(rst), + .axiiv(ether_axiov), + .axiid(ether_axiod), + .axiov(bitorder_axiov), + .axiod(bitorder_axiod)); + + /* firewall -> aggregate */ + reg[1:0] firewall_axiod; + reg firewall_axiov; + + firewall f( + .clk(clk), + .rst(rst), + .axiiv(bitorder_axiov), + .axiid(bitorder_axiod), + .axiov(firewall_axiov), + .axiod(firewall_axiod)); + + /* aggregate output */ + reg[47:0] aggregate_axiod; + reg aggregate_axiov; + + aggregate a( + .clk(clk), + .rst(rst), + .axiiv(firewall_axiov), + .axiid(firewall_axiod), + .axiov(aggregate_axiov), + .axiod(aggregate_axiod)); + + /* cksum -> top_level */ + reg cksum_done; + reg cksum_kill; + + cksum c( + .clk(clk), + .rst(rst), + .axiiv(ether_axiov), + .axiid(ether_axiod), + .done(cksum_done), + .kill(cksum_kill)); + + // state machine + localparam IDLE = 0; + localparam WAIT_FOR_DATA = 1; + localparam WAIT_FOR_FCS = 2; + + reg [1:0] state = IDLE; + + initial valid = 0; + initial data = 0; + + always @(posedge clk) begin + valid <= 0; + + if(state == IDLE) begin + if(crsdv) state <= WAIT_FOR_DATA; + end + + else if(state == WAIT_FOR_DATA) begin + if(aggregate_axiov) begin + state <= WAIT_FOR_FCS; + ethertype <= aggregate_axiod[47:32]; + data <= aggregate_axiod[31:0]; + + end + + // if aggregate never gives us data, go back to idle when the packet ends + else if(cksum_done) state <= IDLE; + end + + else if(state == WAIT_FOR_FCS) begin + if(cksum_done) begin + state <= IDLE; + valid <= ~cksum_kill; + end + end + end +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/src/manta/mac_tx.v b/src/manta/mac_tx.v new file mode 100644 index 0000000..f7b39f0 --- /dev/null +++ b/src/manta/mac_tx.v @@ -0,0 +1,248 @@ +`default_nettype none +`timescale 1ns/1ps + +module mac_tx ( + input wire clk, + + // TODO: make this variable width + input wire [15:0] data, + input wire [15:0] ethertype, + input wire start, + + output reg txen, + output reg [1:0] txd); + + // packet magic numbers + localparam PREAMBLE = {7{8'b01010101}}; + localparam SFD = 8'b11010101; + parameter SRC_MAC = 48'h69_69_69_69_69_69; + parameter DST_MAC = 48'hFF_FF_FF_FF_FF_FF; + + // all lengths are in units of dibits, hence all the mulitplies by four + localparam PREAMBLE_LEN = 7 * 4; + localparam SFD_LEN = 1 * 4; + localparam SRC_MAC_LEN = 6 * 4; + localparam DST_MAC_LEN = 6 * 4; + localparam ETHERTYPE_LEN = 2 * 4; + localparam PAYLOAD_LEN = 2 * 4; + // localparam ZERO_PAD_LEN = (46 * 4) - PAYLOAD_LEN ; // minimum payload size is 46 bytes + localparam ZERO_PAD_LEN = (46 * 4) - PAYLOAD_LEN + 4; // minimum payload size is 46 bytes + localparam FCS_LEN = 4 * 4; + localparam IPG_LEN = 96 / 2; + + // TODO: make crc and bitorder modules not need reset + reg rst = 1; + always @(posedge clk) rst <= 0; + + reg [1:0] bitorder_axiid; + reg [1:0] bitorder_axiod; + reg bitorder_axiiv; + reg bitorder_axiov; + + bitorder bitorder ( + .clk(clk), + .rst(rst), + + .axiiv(bitorder_axiiv), + .axiid(bitorder_axiid), + + .axiov(bitorder_axiov), + .axiod(bitorder_axiod)); + + reg crc_rst = 1; + reg crc_axiiv = 0; + reg [31:0] crc_axiod; + + crc32 crc ( + .clk(clk), + .rst(crc_rst), + + .axiiv(crc_axiiv), + .axiid(bitorder_axiod), + + // TODO: remove axiov from crc32 module, it's always valid + .axiov(), + .axiod(crc_axiod)); + + + // state machine + reg [8:0] counter = 0; + reg [3:0] state = 0; + + localparam IDLE_STATE = 0; + localparam PREAMBLE_STATE = 1; + localparam SFD_STATE = 2; + localparam DST_MAC_STATE = 3; + localparam SRC_MAC_STATE = 4; + localparam ETHERTYPE_STATE = 5; + localparam PAYLOAD_STATE = 6; + localparam ZERO_PAD_STATE = 7; + localparam FCS_STATE = 8; + localparam IPG_STATE = 9; + + + // sequential logic manages the state machine + always @(posedge clk) begin + counter <= counter + 1; + crc_rst <= 0; + + if(state == IDLE_STATE) begin + counter <= 0; + crc_axiiv <= 0; + if(start) state <= PREAMBLE_STATE; + end + + else if(state == PREAMBLE_STATE) begin + if(counter == PREAMBLE_LEN - 1) begin + counter <= 0; + state <= SFD_STATE; + end + end + + else if(state == SFD_STATE) begin + if(counter == SFD_LEN - 1) begin + counter <= 0; + state <= DST_MAC_STATE; + end + end + + else if(state == DST_MAC_STATE) begin + // this is because the crc module lags behind the FSM, + // as it has to go through bitorder first + if(counter == 3) crc_axiiv <= 1; + + if(counter == DST_MAC_LEN - 1) begin + counter <= 0; + state <= SRC_MAC_STATE; + end + end + + else if(state == SRC_MAC_STATE) begin + if(counter == SRC_MAC_LEN - 1) begin + counter <= 0; + state <= ETHERTYPE_STATE; + end + end + + else if(state == ETHERTYPE_STATE) begin + if(counter == ETHERTYPE_LEN - 1) begin + counter <= 0; + state <= PAYLOAD_STATE; + end + end + + else if(state == PAYLOAD_STATE) begin + if(counter == PAYLOAD_LEN - 1) begin + counter <= 0; + state <= ZERO_PAD_STATE; + end + end + + else if(state == ZERO_PAD_STATE) begin + if(counter == ZERO_PAD_LEN - 1) begin + crc_axiiv <= 0; + counter <= 0; + state <= FCS_STATE; + end + end + + else if(state == FCS_STATE) begin + if(counter == FCS_LEN - 1) begin + counter <= 0; + state <= IPG_STATE; + end + end + + else if(state == IPG_STATE) begin + if(counter == IPG_LEN - 1) begin + crc_rst <= 1; + counter <= 0; + state <= IDLE_STATE; + end + end + end + + // combinational logic handles the pipeline + always @(*) begin + case (state) + IDLE_STATE: begin + bitorder_axiiv = 0; + bitorder_axiid = 0; + txen = 0; + txd = 0; + end + + PREAMBLE_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = PREAMBLE[2*(PREAMBLE_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + SFD_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = SFD[2*(SFD_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + DST_MAC_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = DST_MAC[2*(DST_MAC_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + SRC_MAC_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = SRC_MAC[2*(SRC_MAC_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + ETHERTYPE_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = ethertype[2*(ETHERTYPE_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + PAYLOAD_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = data[2*(PAYLOAD_LEN-counter)-1-:2]; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + ZERO_PAD_STATE: begin + bitorder_axiiv = 1; + bitorder_axiid = 0; + txen = bitorder_axiov; + txd = bitorder_axiod; + end + + FCS_STATE: begin + bitorder_axiiv = 0; + bitorder_axiid = 0; + txen = 1; + txd = {crc_axiod[2*(FCS_LEN-counter)-2], crc_axiod[2*(FCS_LEN-counter)-1]}; + end + + IPG_STATE: begin + bitorder_axiiv = 0; + bitorder_axiid = 0; + txen = 0; + txd = 0; + end + + default: begin + bitorder_axiiv = 0; + bitorder_axiid = 0; + txen = 0; + txd = 0; + end + endcase + end +endmodule + +`default_nettype wire \ No newline at end of file diff --git a/test/functional_sim/mac_tb.sv b/test/functional_sim/mac_tb.sv new file mode 100644 index 0000000..9517d05 --- /dev/null +++ b/test/functional_sim/mac_tb.sv @@ -0,0 +1,76 @@ +`default_nettype none +`timescale 1ns/1ps + +module mac_tb(); + logic clk; + + always begin + #5; + clk = !clk; + end + + logic crsdv; + logic [1:0] rxd; + + logic txen; + logic [1:0] txd; + + logic [15:0] mtx_data; + logic [15:0] mtx_ethertype; + logic mtx_start; + + logic [15:0] mrx_data; + logic [15:0] mrx_ethertype; + logic mrx_valid; + + mac_tx mtx ( + .clk(clk), + + .data(mtx_data), + .ethertype(mtx_ethertype), + .start(mtx_start), + + .txen(txen), + .txd(txd)); + + assign rxd = txd; + assign crsdv = txen; + + mac_rx mrx ( + .clk(clk), + + .crsdv(crsdv), + .rxd(rxd), + + .data(mrx_data), + .ethertype(mrx_ethertype), + .valid(mrx_valid)); + + initial begin + $dumpfile("mac_tb.vcd"); + $dumpvars(0, mac_tb); + clk = 0; + mtx_ethertype = 0; + mtx_data = 0; + mtx_start = 0; + #10; + + for (int i=0; i<128; i=i+1) begin + mtx_data = i; + mtx_ethertype = i; + mtx_start = 0; + #10; + mtx_start = 1; + #10; + mtx_start = 0; + while(!mrx_valid) #10; + + #1000; + + assert(mrx_data == i) else $error("data mismatch!"); + end + $finish(); + end + +endmodule +`default_nettype wire \ No newline at end of file