add ethernet_tx/rx, semi-working in hardware
This commit is contained in:
parent
0bb3f9c74a
commit
c507f795f1
5
Makefile
5
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
cores:
|
||||
my_lut_ram:
|
||||
type: lut_ram
|
||||
size: 64
|
||||
|
||||
uart:
|
||||
port: "auto"
|
||||
baudrate: 115200
|
||||
clock_freq: 100000000
|
||||
|
|
@ -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}")
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue