tidy up mac stack

This commit is contained in:
Fischer Moseley 2023-04-26 00:15:46 -04:00
parent 6210e3cc39
commit 7cd8a2cfa5
13 changed files with 287 additions and 403 deletions

37
doc/ethernet.md Normal file
View File

@ -0,0 +1,37 @@
ok so the way the new packets work is:
- everything uses the same ethertype - that's configured once, in manta.yaml, and is set as a parameter in each of the rx and tx stacks
- we do [addr] [data] for incoming write messages, and [addr] for incoming read messages.
- we do [data] for outgoing read responses. this means that:
- we need to detect packet length on mac_rx
- packets coming out of the FPGA are fixed-width, mac_tx will always send out 2 bytes of data
- packets going into the FPGA are guarunteed to be longer than packets coming out of the FPGA
- actually this doesn't make a lot of sense - we're going to be padding anyway, so this really just introduces extra complexity for us. let's just do
something like [rw] [addr] [data]
- since we know that we're _always_ going to get in at least 60 bytes of content and each message only contains like
- we could say that in the future since we're using a fixed ethertype and can detect the paket length based on the crsdv line, we could concevably
stack a bunch of [rw] [addr] [data] things together in the same packet - and creep right up to the ethernet MTU. but we'll file that along the 'other stuff'
and go from there. for now let's just pull 1 + 2 + 2 = 5 bytes = 40 bits into aggregate and see what happens.
- ok so then updated mac_rx is:
- ether, with the reset removed from it
- bitorder, with the reset removed from it
- firewall, but checks the destination MAC of the packet in addition to the ethertype
- transaction, which turns the packets coming in into rw/addr/data triplets. this is then outputted to the top level of mac_rx
- and the updated mac_tx is:
- just the same, except we just put the busficiation logic inside it. so then instead of having start we do the logic with rw_i and valid_i ourselves,
and buffer thee data ourselves
- so then we just have mac_tx and mac_rx in the manta core chain. which feels good.
previous ideas:
- how to do variable length detection? right now our current stack is not well suited for that
- keeping in line with the existing stack, we want to progressively take out chunks as time goes on.
- i think we should modify firewall to check ethertype in addition to mac address also get rid of the reset while we're at it
- because it's jaycode, probably going to be easier to rewrite from scratch to preserve style and sanity. i don't have anything to prove
- we can use the 205 checkers for this, ironcially enough
- i think we should modify aggregate to get both the payload and length. the payload is clocked in dibit-by-dibit, so we'll want to grab the

View File

@ -21,7 +21,7 @@ def read_register(addr):
sniffer.start()
from time import sleep
time.sleep(0.1)
sendp(pkt, iface=ifc)
sendp(pkt, iface=ifc, verbose = 0)
results = sniffer.stop()
assert len(results) == 1, "Received more packets than expected!"
@ -44,7 +44,7 @@ def write_register(addr, data):
pkt = pkt / msg
pkt.load = msg
sendp(pkt, iface=ifc)
sendp(pkt, iface=ifc, verbose = 0)
def read_batch(addrs):
pkts = []
@ -67,7 +67,7 @@ def read_batch(addrs):
from time import sleep
time.sleep(0.1)
sendp(pkts, iface=ifc)
sendp(pkts, iface=ifc, verbose = 0)
sniffer.join()
results = sniffer.results
@ -100,26 +100,25 @@ def write_batch(addrs, data):
pkt = pkt / msg
pkt.load = msg
sendp(pkts, iface=ifc)
sendp(pkts, iface=ifc, verbose = 0)
from time import sleep
if __name__ == "__main__":
# for addr in range(64):
# data = addr
# write_register(addr, data)
for addr in range(64):
data = addr
write_register(addr, data)
retval = read_register(addr)
if retval != addr:
print(f"ERROR: sent {data} got {retval}")
# retval = read_register(addr)
# if retval != addr:
# print("oops")
else:
print(f"SUCCESS: sent {data} got {retval}")
# else:
# print(f"yay addr: {addr} worked!")
addrs = [i for i in range(64)]
datas = addrs
write_batch(addrs, datas)
print("done")
retvals = read_batch(addrs)
print(retvals)
# addrs = [i for i in range(64)]
# datas = addrs
# write_batch(addrs, datas)
# print("done")
# retvals = read_batch(addrs)
# print(retvals)

View File

@ -27,7 +27,10 @@ module manta(
output reg txen,
output reg [1:0] txd);
ethernet_rx erx (
ethernet_rx # (
.FPGA_MAC(48'h69_69_5A_06_54_91),
.ETHERTYPE(16'h88_B5)
) erx (
.clk(clk),
.crsdv(crsdv),
@ -64,7 +67,11 @@ module manta(
reg my_lut_ram_btx_rw;
reg my_lut_ram_btx_valid;
ethernet_tx etx (
ethernet_tx #(
.FPGA_MAC(48'h69_69_5A_06_54_91),
.HOST_MAC(48'h00_E0_4C_68_1E_0C),
.ETHERTYPE(16'h88_B5)
) etx (
.clk(clk),
.txen(txen),

View File

@ -7,54 +7,36 @@
* 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);
module aggregate (
input wire clk,
input wire [1:0] axiid,
input wire axiiv,
/* 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;
output reg [47:0] axiod,
output reg 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;
reg [31:0] counter;
assign axiov = counter == `AGR_SHOW;
always_ff @(posedge clk) begin: COUNTER
if (rst || !axiiv) counter <= 32'b0;
always @(posedge clk) begin: COUNTER
if (!axiiv) counter <= 32'b0;
else counter <= counter + 2;
end
always_ff @(posedge clk) begin: AXIOD
if (rst || !axiiv) axiod <= 32'b0;
always @(posedge clk) begin: AXIOD
if (!axiiv) axiod <= 32'b0;
else if (counter < `AGR_MAX && axiiv)
axiod[`AGR_MAX - counter - 2 +: 2] <= axiid;
end

View File

@ -1,39 +1,19 @@
`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);
module bitorder (
input wire clk,
/* batteries */
input logic clk, rst;
input wire axiiv,
input wire [1:0] axiid,
/* 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;
output reg [1:0] axiod,
output reg axiov);
/* Two registers to hold data coming in off the wire,
* byte by byte. This is where we'll buffer until
@ -42,97 +22,91 @@ module bitorder(clk, rst, axiiv, axiid, axiod, axiov);
* order using one register. Meanwhile, we'll start
* receiving into the other register - dual buffers.
*/
logic[7:0] bufa, bufb;
reg [7:0] bufa = 8'b0;
reg [7:0] bufb = 8'b0;
/* 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;
reg [2:0] countera = 3'b0;
reg [2:0] counterb = 3'b0;
/* 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;
reg [1:0] state = `BO_EMPTYB;
always_comb begin: AXIOV
always @(*) begin: AXIOV
if (state == `BO_SENDA || state == `BO_SENDB) axiov = 1'b1;
else axiov = 1'b0;
end
always_comb begin: AXIOD
always @(*) 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
always @(posedge clk) begin: BUFFERIN
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
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
always @(posedge clk) begin: STATES
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_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;
`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
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;
`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
if (axiiv) begin
if (counterb == 3'h6)
state <= `BO_SENDB;
else counterb <= counterb + 2;
end
end
endcase
end
endmodule
`default_nettype wire

View File

@ -19,52 +19,37 @@
`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);
module cksum (
input wire clk,
input wire [1:0] axiid,
input wire axiiv,
/* batteries */
input logic clk, rst;
output reg done,
output reg kill);
/* 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;
reg [31:0] crcd;
reg crcv;
/* Decoupled logic to reset the CRC module independently
* Used to compute multiple CRCs back to back
*/
logic crcrst;
reg crcrst;
/* Our finite state machine - bonus points if you can identify
* whether this is a Moore or Mealy FSM!
*/
logic[1:0] state;
reg [1:0] state = `CK_FRESH;
crc32 cksum(.clk(clk),
.rst(crcrst | rst),
.axiiv(axiiv),
.axiid(axiid),
.axiov(crcv),
.axiod(crcd));
crc32 cksum(
.clk(clk),
.rst(crcrst),
.axiiv(axiiv),
.axiid(axiid),
.axiov(crcv),
.axiod(crcd));
always_ff @(posedge clk) begin: OUTPUTS
if (rst || axiiv) begin
always @(posedge clk) begin: OUTPUTS
if (axiiv) begin
done <= 1'b0;
kill <= 1'b0;
crcrst <= 1'b0;
@ -72,22 +57,19 @@ module cksum(clk, rst, axiid, axiiv, done, kill);
if (state == `CK_COMPUTING && !axiiv) begin
done <= 1'b1;
crcrst <= 1'b1;
kill <= (crcd != `MAGIC_CHECK);
end
if (crcd == `MAGIC_CHECK) kill <= 1'b0;
else kill <= 1'b1;
end else crcrst <= 1'b0;
else crcrst <= 1'b0;
end
end
always_ff @(posedge clk) begin: FSM
if (rst) state <= `CK_FRESH;
else begin
case (state)
always @(posedge clk) begin: FSM
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
endcase
end
endmodule

View File

@ -1,27 +1,6 @@
`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
@ -35,60 +14,19 @@
`define PREAM_LAST 2'b11
`define PREAM_BAD 2'b10
module ether(clk, rst, rxd, crsdv, axiov, axiod);
module ether (
input wire clk,
input wire [1:0] rxd,
input wire crsdv,
input logic clk, rst;
output reg axiov,
output reg [1:0] axiod);
/* 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;
reg [4:0] count;
reg [2:0] state;
/* 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;
reg [1:0] preamex;
reg preamok, start;
always @(*) begin: PREAM
if (count == `PREAM_SIZE - 1) preamex = `PREAM_LAST;
@ -105,34 +43,34 @@ module ether(clk, rst, rxd, crsdv, axiov, axiod);
else count <= 0;
end
initial begin
axiod = 2'b0;
axiov = 1'b0;
state = 3'b0;
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;
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_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
`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
endmodule

View File

@ -13,27 +13,29 @@ module ethernet_rx (
output reg valid_o
);
// we know if the packet is a read or write
// based on the ethertype.
parameter FPGA_MAC = 0;
parameter ETHERTYPE = 0;
reg [15:0] ethertype;
reg [31:0] data;
reg valid;
mac_rx mrx (
mac_rx #(
.DST_MAC(48'h69_69_5A_06_54_91),
.ETHERTYPE(16'h88_B5)
) mrx (
.clk(clk),
.crsdv(crsdv),
.rxd(rxd),
.ethertype(ethertype),
.data(data),
.payload(payload),
.length(length)
.valid(valid));
assign addr_o = data[31:16];
assign wdata_o = data[15:0];
assign rw_o = (ethertype == 4);
assign valid_o = valid && ((ethertype == 4) || (ethertype == 2));
assign addr_o = payload[31:16];
assign wdata_o = payload[15:0];
assign rw_o = (length == 4);
assign valid_o = valid && ((length == 4) || (length == 2));
endmodule

View File

@ -1,25 +1,35 @@
`default_nettype none
`timescale 1ns/1ps
module ethernet_tx(
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
input wire valid_i,
output reg txen,
output reg [1:0] txd
);
reg [15:0] data_buf = 0;
always @(posedge clk) if(~rw_i && valid_i) data_buf <= rdata_i;
parameter FPGA_MAC = 0;
parameter HOST_MAC = 0;
parameter ETHERTYPE = 0;
mac_tx mtx (
reg [15:0] rdata_buf = 0;
always @(posedge clk)
if(~rw_i && valid_i) rdata_buf <= rdata_i;
mac_tx #(
.SRC_MAC(FPGA_MAC),
.DST_MAC(HOST_MAC),
.ETHERTYPE(ETHERTYPE),
.PAYLOAD_LENGTH_BYTES(2)
) mtx (
.clk(clk),
.data(data_buf),
.ethertype(16'h2),
.payload(rdata_buf),
.start(~rw_i && valid_i),
.txen(txen),

View File

@ -1,62 +1,21 @@
`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);
module firewall (
input wire clk,
input wire axiiv,
input wire [1:0] axiid,
/* batteries */
input logic clk, rst;
output reg axiov,
output reg [1:0] axiod);
/* 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;
parameter ETHERTYPE = 0;
parameter FPGA_MAC = 0;
/* Buffers to hold our MAC address in the reverse order,
* to make comparison easier than it otherwise would be
@ -79,9 +38,9 @@ module firewall(clk, rst, axiiv, axiid, axiov, axiod);
*/
logic matchme, matchbcast;
assign me = `FW_ME;
assign me = FPGA_MAC;
always_ff @(posedge clk) begin: MATCH
always @(posedge clk) begin: MATCH
if (counter == 32'b0) begin
matchme <= 1'b1;
matchbcast <= 1'b1;
@ -101,7 +60,7 @@ module firewall(clk, rst, axiiv, axiid, axiov, axiod);
end
end
always_comb begin: AXIOUT
always @(*) begin: AXIOUT
if (counter >= `FW_DATASTART && (matchme | matchbcast)) begin
axiod = axiid;
axiov = axiiv;
@ -111,7 +70,7 @@ module firewall(clk, rst, axiiv, axiid, axiov, axiod);
end
end
always_ff @(posedge clk) begin: COUNTER
always @(posedge clk) begin: COUNTER
if (axiiv) counter <= counter + 2;
else counter <= 32'b0;
end

View File

@ -7,69 +7,60 @@ module mac_rx (
input wire crsdv,
input wire [1:0] rxd,
output reg [15:0] ethertype,
output reg [31:0] data,
output reg [39:0] payload,
output reg valid);
// TODO: rewrite modules to not need external reset
reg rst = 1;
always @(posedge clk) rst <= 0;
parameter FPGA_MAC = 0;
parameter ETHERTYPE = 0;
/* ether -> { cksum, bitorder } */
reg[1:0] ether_axiod;
reg [1:0] ether_axiod;
reg ether_axiov;
ether e(
ether e (
.clk(clk),
.rst(rst),
.rxd(rxd),
.crsdv(crsdv),
.axiov(ether_axiov),
.axiod(ether_axiod));
/* bitorder -> firewall */
reg[1:0] bitorder_axiod;
reg [1:0] bitorder_axiod;
reg bitorder_axiov;
bitorder b(
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 [1:0] firewall_axiod;
reg firewall_axiov;
firewall f(
firewall #(
.FPGA_MAC(FPGA_MAC),
.ETHERTYPE(ETHERTYPE)
) 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 [47:0] aggregate_axiod;
reg aggregate_axiov;
aggregate a(
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(
cksum c (
.clk(clk),
.rst(rst),
.axiiv(ether_axiov),
.axiid(ether_axiod),
.done(cksum_done),
@ -83,7 +74,7 @@ module mac_rx (
reg [1:0] state = IDLE;
initial valid = 0;
initial data = 0;
initial payload = 0;
always @(posedge clk) begin
valid <= 0;
@ -95,12 +86,11 @@ module mac_rx (
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];
payload <= aggregate_axiod[31:0];
end
// if aggregate never gives us data, go back to idle when the packet ends
// if aggregate never gives us data,
// go back to idle when the packet ends
else if(cksum_done) state <= IDLE;
end

View File

@ -4,9 +4,7 @@
module mac_tx (
input wire clk,
// TODO: make this variable width
input wire [15:0] data,
input wire [15:0] ethertype,
input wire [(8 * PAYLOAD_LENGTH_BYTES)-1:0] payload,
input wire start,
output reg txen,
@ -15,8 +13,10 @@ module mac_tx (
// 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;
parameter [47:0] SRC_MAC = 0;
parameter [47:0] DST_MAC = 0;
parameter [15:0] ETHERTYPE = 0;
parameter PAYLOAD_LENGTH_BYTES = 0;
// all lengths are in units of dibits, hence all the mulitplies by four
localparam PREAMBLE_LEN = 7 * 4;
@ -24,16 +24,11 @@ module mac_tx (
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 PAYLOAD_LEN = PAYLOAD_LENGTH_BYTES * 4;
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;
@ -41,7 +36,6 @@ module mac_tx (
bitorder bitorder (
.clk(clk),
.rst(rst),
.axiiv(bitorder_axiiv),
.axiid(bitorder_axiid),
@ -202,14 +196,14 @@ 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
PAYLOAD_STATE: begin
bitorder_axiiv = 1;
bitorder_axiid = data[2*(PAYLOAD_LEN-counter)-1-:2];
bitorder_axiid = payload[2*(PAYLOAD_LEN-counter)-1-:2];
txen = bitorder_axiov;
txd = bitorder_axiod;
end

View File

@ -1,6 +1,11 @@
`default_nettype none
`timescale 1ns/1ps
`define FPGA_MAC 48'h69_69_5A_06_54_91
`define HOST_MAC 48'h00_E0_4C_68_1E_0C
`define ETHERTYPE 16'h88_B5
module mac_tb();
logic clk;
@ -15,19 +20,24 @@ module mac_tb();
logic txen;
logic [1:0] txd;
logic [15:0] mtx_data;
logic [15:0] mtx_ethertype;
// this testbench makes sure that our rx pipeline is good,
// so we'll have mtx simulate the host machine and blast
// 5 bytes (plus padding) onto our fake RMII
logic [39:0] mtx_payload;
logic mtx_start;
logic [15:0] mrx_data;
logic [15:0] mrx_ethertype;
logic [39:0] mrx_payload;
logic mrx_valid;
mac_tx mtx (
mac_tx #(
.SRC_MAC(`HOST_MAC),
.DST_MAC(`FPGA_MAC),
.ETHERTYPE(`ETHERTYPE),
.PAYLOAD_LENGTH_BYTES(5)
) mtx (
.clk(clk),
.data(mtx_data),
.ethertype(mtx_ethertype),
.payload(mtx_payload),
.start(mtx_start),
.txen(txen),
@ -36,28 +46,28 @@ module mac_tb();
assign rxd = txd;
assign crsdv = txen;
mac_rx mrx (
mac_rx #(
.FPGA_MAC(`FPGA_MAC),
.ETHERTYPE(`ETHERTYPE)
) mrx (
.clk(clk),
.crsdv(crsdv),
.rxd(rxd),
.data(mrx_data),
.ethertype(mrx_ethertype),
.payload(mrx_payload),
.valid(mrx_valid));
initial begin
$dumpfile("mac_tb.vcd");
$dumpvars(0, mac_tb);
clk = 0;
mtx_ethertype = 0;
mtx_data = 0;
mtx_payload = 0;
mtx_start = 0;
#10;
for (int i=0; i<128; i=i+1) begin
mtx_data = i;
mtx_ethertype = i;
for (int i=0; i<32; i=i+1) begin
mtx_payload = i;
mtx_start = 0;
#10;
mtx_start = 1;
@ -67,7 +77,7 @@ module mac_tb();
#1000;
assert(mrx_data == i) else $error("data mismatch!");
assert(mrx_payload == i) else $error("data mismatch!");
end
$finish();
end