tidy up mac stack
This commit is contained in:
parent
6210e3cc39
commit
7cd8a2cfa5
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue