Compare commits
2 Commits
master
...
feature/tr
Author | SHA1 | Date | |
---|---|---|---|
70da0534c0 | |||
e441663586 |
@ -1,10 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<com.sigasi.hdt.shared.librarymapping.model:LibraryMappings xmlns:com.sigasi.hdt.shared.librarymapping.model="com.sigasi.hdt.vhdl.scoping.librarymapping" Version="2">
|
||||
<Mappings Location="bench" Library="bench"/>
|
||||
<Mappings Location="cocotb" Library="cocotb"/>
|
||||
<Mappings Location="design" Library="design"/>
|
||||
<Mappings Location="Common Libraries/IEEE" Library="ieee"/>
|
||||
<Mappings Location="Common Libraries/IEEE Synopsys" Library="ieee"/>
|
||||
<Mappings Location="" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries/vunit" Library="not mapped"/>
|
||||
<Mappings Location="cocotb/__pycache__" Library="not mapped"/>
|
||||
<Mappings Location="cocotb/sim_build" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries/STD" Library="std"/>
|
||||
<Mappings Location="trashernet" Library="trashernet"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IP_complete_nomac.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_Complete_nomac.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_REQ.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_STORE_br.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_SYNC.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_types.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arpv2.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/axi.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/ipv4_types.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/tx_arbitrator.vhd" Library="udpip"/>
|
||||
<Mappings Location="Common Libraries/vunit/vunit/vhdl" Library="vunit_lib"/>
|
||||
<Mappings Location="Common Libraries/src" Library="xp2"/>
|
||||
</com.sigasi.hdt.shared.librarymapping.model:LibraryMappings>
|
||||
|
27
.project
27
.project
@ -5,6 +5,16 @@
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/com.sigasi.hdt.toolchains.vunit.builder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
|
||||
<arguments>
|
||||
@ -12,9 +22,9 @@
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.sigasi.hdt.verilog.ui.verilogNature</nature>
|
||||
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
|
||||
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
|
||||
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
|
||||
</natures>
|
||||
<linkedResources>
|
||||
<link>
|
||||
@ -22,6 +32,11 @@
|
||||
<type>2</type>
|
||||
<locationURI>virtual:/virtual</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
|
||||
<type>1</type>
|
||||
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/IEEE</name>
|
||||
<type>2</type>
|
||||
@ -37,5 +52,15 @@
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/src</name>
|
||||
<type>2</type>
|
||||
<location>/usr/local/diamond/3.12/cae_library/simulation/vhdl/xp2/src</location>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/vunit</name>
|
||||
<type>2</type>
|
||||
<locationURI>PARENT-1-PROJECT_LOC/vunit</locationURI>
|
||||
</link>
|
||||
</linkedResources>
|
||||
</projectDescription>
|
||||
|
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
@ -0,0 +1,2 @@
|
||||
VUnitScriptLocation=vunit/run.py
|
||||
eclipse.preferences.version=1
|
@ -1,2 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
isEnabled=true
|
@ -1 +0,0 @@
|
||||
<project>=sv2012
|
@ -1,2 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
isEnabled=true
|
@ -1,5 +1,5 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//Common\ Libraries/IEEE=UTF-8
|
||||
encoding//Common\ Libraries/IEEE\ Synopsys=UTF-8
|
||||
encoding//Common\ Libraries/STD=UTF-8
|
||||
encoding//Common\ Libraries=UTF-8
|
||||
encoding//Common\ Libraries/IEEE=utf-8
|
||||
encoding//Common\ Libraries/IEEE\ Synopsys=utf-8
|
||||
encoding//Common\ Libraries/STD=utf-8
|
||||
encoding/Common\ Libraries=utf-8
|
||||
|
13
README.MD
13
README.MD
@ -4,10 +4,9 @@
|
||||
|
||||
Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to cover all OSI layers:
|
||||
|
||||
* Layer 1, Physical: `trashernet_phy`, (`trashernet_rmii`)
|
||||
* Layer 1, Physical: `trashernet_phy`
|
||||
* Layer 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
|
||||
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp`
|
||||
* Layer 4, Transport: `trashernet_udp`, `trashernet_udpprot`
|
||||
|
||||
When writing it, the following were the main design philosophies:
|
||||
|
||||
@ -17,8 +16,6 @@ When writing it, the following were the main design philosophies:
|
||||
|
||||
**IMPORTANT:** This code (and HW suggestions) violate the Ethernet standard in many ways. Do not use in proper (especially commercial) products.
|
||||
|
||||
Note: The `trashernet_rmii` component uses a standard RMII Ethernet PHY instead of the hardware suggestions below. If you are looking for the true Trashernet experience, choose the `trashernet_phy` instead.
|
||||
|
||||
## Hardware
|
||||
|
||||

|
||||
@ -31,14 +28,6 @@ Notes:
|
||||
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
|
||||
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
|
||||
|
||||
## Stats
|
||||
|
||||
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
|
||||
|
||||
| Configuration | Slices | LUT4 | Registers |
|
||||
| ---------------- | ------ | ---- | --------- |
|
||||
| `trashernet_phy` | 115 | 188 | 91 |
|
||||
|
||||
## License
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
|
@ -19,12 +19,12 @@ end entity bench_crc;
|
||||
|
||||
architecture bench of bench_crc is
|
||||
constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7";
|
||||
signal clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal data : std_logic_vector(7 downto 0);
|
||||
signal data_valid : std_logic;
|
||||
signal crc_clear : std_logic;
|
||||
signal crc_out : std_logic_vector(POLYNOMIAL'range);
|
||||
signal clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal data : std_logic_vector(7 downto 0);
|
||||
signal data_valid : std_logic;
|
||||
signal crc_clear : std_logic;
|
||||
signal crc_out : std_logic_vector(POLYNOMIAL'range);
|
||||
begin
|
||||
crc_inst : entity trashernet.crc
|
||||
generic map(
|
||||
|
@ -30,7 +30,7 @@ package body bench_pkg is
|
||||
wait for 50 ns;
|
||||
end loop;
|
||||
end loop;
|
||||
wait for 16 us; -- IPG
|
||||
wait for 16 us; -- IPG
|
||||
report "Frame TX complete";
|
||||
end procedure send_data;
|
||||
end package body bench_pkg;
|
||||
|
@ -22,14 +22,15 @@ entity bench_trashernet_phy is
|
||||
end entity bench_trashernet_phy;
|
||||
|
||||
architecture bench of bench_trashernet_phy is
|
||||
signal clk : std_logic;
|
||||
signal phy_clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal clk : std_logic;
|
||||
signal phy_clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
signal phy_in : phy_in_t;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
|
||||
|
||||
begin
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
@ -38,14 +39,14 @@ begin
|
||||
F_CLK_PHY => 100000000
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
clk => clk,
|
||||
phy_clk => phy_clk,
|
||||
rst => rst,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
clock_driver : process
|
||||
|
@ -1,218 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- bench_trashernet_mac.vhd : Stimulus-only test bench for the MAC+PHY parts
|
||||
-- Tests TX path through the RX path. Not great, but whatever.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use work.bench_pkg.all;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
library vunit_lib;
|
||||
context vunit_lib.vunit_context;
|
||||
|
||||
entity bench_trashernet_rmii is
|
||||
generic(
|
||||
runner_cfg : string
|
||||
);
|
||||
end entity bench_trashernet_rmii;
|
||||
|
||||
architecture bench of bench_trashernet_rmii is
|
||||
signal clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
signal rmii_ref_clk : std_logic;
|
||||
signal rmii_crs_dv : std_logic;
|
||||
signal rmii_rxd : std_logic_vector(1 downto 0);
|
||||
signal rmii_tx_en : std_logic;
|
||||
signal rmii_txd : std_logic_vector(1 downto 0);
|
||||
|
||||
signal rmii_tx_start : std_logic := '0';
|
||||
signal trashernet_tx_start : std_logic := '0';
|
||||
|
||||
begin
|
||||
trashernet_rmii_inst : entity trashernet.trashernet_rmii
|
||||
generic map(
|
||||
SYSCLK_IS_REFCLK => false
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rmii_ref_clk => rmii_ref_clk,
|
||||
rmii_crs_dv => rmii_crs_dv,
|
||||
rmii_rxd => rmii_rxd,
|
||||
rmii_tx_en => rmii_tx_en,
|
||||
rmii_txd => rmii_txd
|
||||
);
|
||||
|
||||
clockDriver : process
|
||||
constant period : time := 10 ns;
|
||||
begin
|
||||
clk <= '0';
|
||||
wait for period / 2;
|
||||
clk <= '1';
|
||||
wait for period / 2;
|
||||
end process clockDriver;
|
||||
|
||||
test : process is
|
||||
begin
|
||||
rst <= '1';
|
||||
test_runner_setup(runner, runner_cfg);
|
||||
|
||||
wait for 10 ns;
|
||||
rst <= '0';
|
||||
wait for 10 ns;
|
||||
|
||||
while test_suite loop
|
||||
if run("rx") then
|
||||
rmii_tx_start <= not rmii_tx_start;
|
||||
wait until phy_out.rx_active = '1' for 1 ms;
|
||||
assert phy_out.rx_active'event report "Timeout for reception to start";
|
||||
wait until phy_out.rx_active = '0' for 1 ms;
|
||||
assert phy_out.rx_active'event report "Timeout for reception to end";
|
||||
wait for 1 us;
|
||||
|
||||
elsif run("tx") then
|
||||
trashernet_tx_start <= not trashernet_tx_start;
|
||||
wait until phy_out.tx_active = '1' for 1 ms;
|
||||
assert phy_out.tx_active'event report "Timeout for transmission to start";
|
||||
wait until phy_out.tx_active = '0' for 1 ms;
|
||||
assert phy_out.tx_active'event report "Timeout for transmission to stop";
|
||||
|
||||
wait until phy_out.tx_active = '1' for 1 ms;
|
||||
assert phy_out.tx_active'event report "Timeout for transmission to start";
|
||||
wait until phy_out.tx_active = '0' for 1 ms;
|
||||
assert phy_out.tx_active'event report "Timeout for transmission to stop";
|
||||
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
wait for 1 ns;
|
||||
|
||||
test_runner_cleanup(runner);
|
||||
end process test;
|
||||
|
||||
phy_rx : process(clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if phy_out.rx_data_valid = '1' then
|
||||
report "RX data: " & to_hstring(phy_out.rx_data);
|
||||
end if;
|
||||
end if;
|
||||
end process phy_rx;
|
||||
|
||||
rmiirefclk : process
|
||||
constant period : time := 20 ns;
|
||||
begin
|
||||
rmii_ref_clk <= '0';
|
||||
wait for period / 2;
|
||||
rmii_ref_clk <= '1';
|
||||
wait for period / 2;
|
||||
end process rmiirefclk;
|
||||
|
||||
rmii_tx_gen : process is
|
||||
procedure send_frame(data : byte_vector) is
|
||||
variable sr : byte;
|
||||
begin
|
||||
wait until rising_edge(rmii_ref_clk);
|
||||
rmii_rxd <= "01";
|
||||
wait for 2.5 ns;
|
||||
rmii_crs_dv <= '1';
|
||||
|
||||
for i in 0 to 10 loop
|
||||
wait until rmii_ref_clk;
|
||||
end loop;
|
||||
rmii_rxd <= "11";
|
||||
wait until rmii_ref_clk;
|
||||
|
||||
for i in data'range loop
|
||||
sr := data(i);
|
||||
for j in 0 to 3 loop
|
||||
rmii_rxd <= sr(1 downto 0);
|
||||
wait until rising_edge(rmii_ref_clk);
|
||||
sr := "XX" & sr(sr'high downto 2);
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
rmii_crs_dv <= '0';
|
||||
|
||||
wait for 1 us; -- IPG
|
||||
end procedure send_frame;
|
||||
|
||||
begin
|
||||
rmii_crs_dv <= '0';
|
||||
rmii_rxd <= (others => '0');
|
||||
|
||||
wait on rmii_tx_start;
|
||||
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
|
||||
wait;
|
||||
end process rmii_tx_gen;
|
||||
|
||||
trashernet_tx_gen : process is
|
||||
procedure send_frame(data : byte_vector) is
|
||||
begin
|
||||
for i in data'range loop
|
||||
phy_in.tx_data_en <= '1';
|
||||
phy_in.tx_data <= data(i);
|
||||
wait until rising_edge(clk);
|
||||
while not phy_out.tx_data_ack loop
|
||||
wait until rising_edge(clk);
|
||||
end loop;
|
||||
end loop;
|
||||
phy_in.tx_data_en <= '0';
|
||||
wait until rising_edge(clk);
|
||||
end procedure send_frame;
|
||||
begin
|
||||
phy_in.tx_data_en <= '0';
|
||||
wait on trashernet_tx_start;
|
||||
wait until rising_edge(clk);
|
||||
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
|
||||
-- Currently, there's a bug in the CDC preventing single-cycle deassertions from deasserting the transmit enable
|
||||
wait until rising_edge(clk);
|
||||
wait until rising_edge(clk);
|
||||
wait until rising_edge(clk);
|
||||
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
|
||||
end process trashernet_tx_gen;
|
||||
|
||||
rmii_receiver : process(rmii_ref_clk) is
|
||||
variable sr : byte;
|
||||
variable cnt : integer range 0 to 3;
|
||||
variable active : boolean := false;
|
||||
begin
|
||||
if rising_edge(rmii_ref_clk) then
|
||||
if rmii_tx_en then
|
||||
if not active then
|
||||
report "RMII RX start";
|
||||
end if;
|
||||
sr := rmii_txd & sr(sr'high downto 2);
|
||||
if cnt = 3 then
|
||||
cnt := 0;
|
||||
report "RMII RX: " & to_hstring(sr);
|
||||
else
|
||||
cnt := cnt + 1;
|
||||
end if;
|
||||
active := true;
|
||||
else
|
||||
if active then
|
||||
report "RMII RX stop";
|
||||
end if;
|
||||
cnt := 0;
|
||||
active := false;
|
||||
end if;
|
||||
end if;
|
||||
end process rmii_receiver;
|
||||
|
||||
end architecture bench;
|
@ -1,7 +0,0 @@
|
||||
configuration cfg_cocotb_top_hwitl_mac of cocotb_top_hwitl is
|
||||
for bench
|
||||
for top_mac_test_inst : top_hwitl
|
||||
use entity design.top_hwitl(mac);
|
||||
end for;
|
||||
end for;
|
||||
end configuration cfg_cocotb_top_hwitl_mac;
|
@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Set up virtual device using
|
||||
sudo ip link add dev veth1 type veth peer name veth2
|
||||
sudo ip link set up veth1
|
||||
sudo ip link set up veth2
|
||||
sudo ip addr add 192.168.2.1/24 dev veth1
|
||||
sudo ip link set promisc on dev veth1
|
||||
|
@ -29,8 +29,8 @@ end entity ledcon;
|
||||
|
||||
architecture RTL of ledcon is
|
||||
constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3))));
|
||||
signal ms_cnt : integer range 0 to MS_CNT_MAX;
|
||||
signal ms_tick : std_logic;
|
||||
signal ms_cnt : integer range 0 to MS_CNT_MAX;
|
||||
signal ms_tick : std_logic;
|
||||
begin
|
||||
mstimer : process(clk, rst) is
|
||||
begin
|
||||
@ -50,7 +50,7 @@ begin
|
||||
ledblock : block
|
||||
constant LED_CNT_MAX : integer := 50; -- on time, in ms
|
||||
constant LED_CNT_MIN : integer := -50; -- off time, in ms
|
||||
signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
|
||||
signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
|
||||
begin
|
||||
ledgen : if BLINK(i) generate
|
||||
ledproc : process(clk, rst) is
|
||||
|
@ -57,13 +57,13 @@ architecture mac of top_hwitl is
|
||||
signal mac_out : mac_out_t;
|
||||
signal mac_in : mac_in_t;
|
||||
|
||||
type status_t is (IDLE, TX);
|
||||
signal state : status_t;
|
||||
type status_t is (IDLE, TX);
|
||||
signal state : status_t;
|
||||
constant BYTE_CNT_MAX : integer := 100;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
|
||||
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
|
||||
signal tmo : integer range 0 to TMO_MAX;
|
||||
signal tmo : integer range 0 to TMO_MAX;
|
||||
begin
|
||||
trashernet_mac_inst : entity trashernet.trashernet_mac
|
||||
port map(
|
||||
@ -206,10 +206,10 @@ architecture eth of top_hwitl is
|
||||
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
|
||||
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
|
||||
);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
|
||||
signal arp_out : arp_out_t;
|
||||
signal arp_in : arp_in_t;
|
||||
@ -217,45 +217,15 @@ architecture eth of top_hwitl is
|
||||
signal ipv4_out : ipv4_out_t;
|
||||
signal ipv4_in : ipv4_in_t;
|
||||
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPROT_UDP : integer := 1;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPROT_UDP : integer := 1;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
IPROT_ICMP => IPV4_PROTOCOL_ICMP,
|
||||
IPROT_UDP => IPV4_PROTOCOL_UDP
|
||||
);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
||||
|
||||
signal udp_out : udp_out_t;
|
||||
signal udp_in : udp_in_t;
|
||||
|
||||
signal udpprot_rx_out : udpprot_rx_out_vector(0 to 0);
|
||||
signal udpprot_rx_in : udpprot_rx_in_vector(0 to 0);
|
||||
signal udpprot_tx_out : udpprot_tx_out_vector(0 to 0);
|
||||
signal udpprot_tx_in : udpprot_tx_in_vector(0 to 0);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
||||
begin
|
||||
trashernet_udpprot_inst : entity trashernet.trashernet_udpprot
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
udp_out => udp_out,
|
||||
udp_in => udp_in,
|
||||
udpprot_rx_out => udpprot_rx_out,
|
||||
udpprot_rx_in => udpprot_rx_in,
|
||||
udpprot_tx_out => udpprot_tx_out,
|
||||
udpprot_tx_in => udpprot_tx_in
|
||||
);
|
||||
|
||||
trashernet_udp_inst : entity trashernet.trashernet_udp
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_protocol_in => ipv4_protocol_in(IPROT_UDP),
|
||||
ipv4_protocol_out => ipv4_protocol_out(IPROT_UDP),
|
||||
udp_out => udp_out,
|
||||
udp_in => udp_in
|
||||
);
|
||||
|
||||
trashernet_icmp_inst : entity trashernet.trashernet_icmp
|
||||
port map(
|
||||
clk => clk,
|
||||
@ -377,7 +347,7 @@ begin
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
if ethernet_i_out.rx_header_rcv then
|
||||
report "RX ETH-I: " & to_string(to_integer(ethernet_i_out.rx_header.length));
|
||||
report "RX ETH-I: " & to_string(to_integer(ethernet_i_out.rx_length));
|
||||
end if;
|
||||
if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then
|
||||
report "RX ARP";
|
||||
@ -390,7 +360,7 @@ begin
|
||||
report "RX IP error";
|
||||
end if;
|
||||
if ipv4_out.rx_header_rcv then
|
||||
report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_header.protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_header.ip_address);
|
||||
report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_ip_address);
|
||||
end if;
|
||||
end if;
|
||||
end process receiver;
|
||||
@ -405,23 +375,20 @@ begin
|
||||
end process udp;
|
||||
|
||||
tx_udp_p : block
|
||||
type state_t is (IDLE, TX, DONE);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, TX, DONE);
|
||||
signal state : state_t;
|
||||
|
||||
constant PACKET : byte_vector := (
|
||||
x"10", x"00", -- Source port
|
||||
x"04", x"00", -- Destination port
|
||||
x"00", x"15", -- Length
|
||||
x"00", x"00", -- Checksum (not used)
|
||||
x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload
|
||||
);
|
||||
signal sr : byte_vector(PACKET'range);
|
||||
signal cnt : integer range 0 to sr'length - 1;
|
||||
|
||||
begin
|
||||
udpprot_tx_in(0).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
|
||||
udpprot_tx_in(0).tx_destination_port <= x"00FF";
|
||||
udpprot_tx_in(0).tx_source_port <= x"ABCD";
|
||||
udpprot_tx_in(0).tx_length <= to_unsigned(sr'length, 16);
|
||||
udpprot_tx_in(0).tx_data <= sr(0);
|
||||
udpprot_tx_in(0).tx_en <= '1' when state = TX else '0';
|
||||
|
||||
tx_udp : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
@ -434,12 +401,11 @@ begin
|
||||
state <= TX;
|
||||
sr <= PACKET;
|
||||
cnt <= sr'length - 1;
|
||||
report "UDP(0): Start TX";
|
||||
report "UDP: Start TX";
|
||||
end if;
|
||||
|
||||
when TX =>
|
||||
if udpprot_tx_out(0).tx_data_ack then
|
||||
report "UDP(0): byte ack";
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_data_ack then
|
||||
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||
if cnt = 0 then
|
||||
state <= DONE;
|
||||
@ -447,12 +413,12 @@ begin
|
||||
cnt <= cnt - 1;
|
||||
end if;
|
||||
end if;
|
||||
if udpprot_tx_out(0).tx_err_stb then
|
||||
report "UDP(0): TX ERROR";
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
|
||||
report "UDP: TX ERROR";
|
||||
state <= DONE;
|
||||
end if;
|
||||
if udpprot_tx_out(0).tx_ok_stb then
|
||||
report "UDP(0): TX OK stb in TX state -- that shouldn't happen, right?";
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then
|
||||
report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
|
||||
end if;
|
||||
|
||||
when DONE =>
|
||||
@ -461,29 +427,12 @@ begin
|
||||
end case;
|
||||
end if;
|
||||
end process tx_udp;
|
||||
ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0';
|
||||
ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
|
||||
ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16);
|
||||
ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0);
|
||||
end block tx_udp_p;
|
||||
|
||||
udpprot_rx_in(0).port_bind <= x"0400";
|
||||
|
||||
rx_udp_p : block
|
||||
begin
|
||||
rx_udp : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
if udpprot_rx_out(0).rx_header_rcv then
|
||||
report "UDP(0): RX on port " & --
|
||||
to_string(to_integer(udpprot_rx_out(0).rx_header.destination_port)) & " from port " & --
|
||||
to_string(to_integer(udpprot_rx_out(0).rx_header.source_port));
|
||||
end if;
|
||||
if udpprot_rx_out(0).rx_data_valid then
|
||||
report "UDP(0): RX: " & to_hstring(udpprot_rx_out(0).rx_data);
|
||||
end if;
|
||||
end if;
|
||||
end process rx_udp;
|
||||
end block rx_udp_p;
|
||||
|
||||
icmp : process(clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
|
@ -68,16 +68,16 @@ architecture rtl of top_mac_test is
|
||||
signal mac_out : mac_out_t;
|
||||
signal mac_in : mac_in_t;
|
||||
|
||||
type status_t is (IDLE, TX);
|
||||
signal state : status_t;
|
||||
type status_t is (IDLE, TX);
|
||||
signal state : status_t;
|
||||
constant BYTE_CNT_MAX : integer := 100;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
|
||||
signal button_n_sync : std_logic_vector(button_n'range);
|
||||
signal button : std_logic_vector(button_n'range);
|
||||
|
||||
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
|
||||
signal tmo : integer range 0 to TMO_MAX;
|
||||
signal tmo : integer range 0 to TMO_MAX;
|
||||
begin
|
||||
trashernet_mac_inst : entity trashernet.trashernet_mac
|
||||
port map(
|
||||
@ -190,7 +190,7 @@ begin
|
||||
if mac_out.tx_mac_data_ack then
|
||||
if byte_cnt = 1 then
|
||||
mac_in.tx_mac_data_en <= '0';
|
||||
state <= IDLE;
|
||||
state <= IDLE;
|
||||
else
|
||||
byte_cnt <= byte_cnt - 1;
|
||||
end if;
|
||||
|
@ -79,10 +79,10 @@ architecture rtl of top_pingable is
|
||||
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
|
||||
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
|
||||
);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
|
||||
signal arp_out : arp_out_t;
|
||||
signal arp_in : arp_in_t;
|
||||
@ -90,12 +90,12 @@ architecture rtl of top_pingable is
|
||||
signal ipv4_out : ipv4_out_t;
|
||||
signal ipv4_in : ipv4_in_t;
|
||||
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
IPROT_ICMP => IPV4_PROTOCOL_ICMP
|
||||
);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
||||
|
||||
signal button_n_sync : std_logic_vector(button_n'range);
|
||||
signal button : std_logic_vector(button_n'range);
|
||||
|
@ -1,2 +0,0 @@
|
||||
vunit_hdl
|
||||
cocotb
|
1
sw/trashernet-as/.gitignore
vendored
Normal file
1
sw/trashernet-as/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/**
|
491
sw/trashernet-as/Cargo.lock
generated
Normal file
491
sw/trashernet-as/Cargo.lock
generated
Normal file
@ -0,0 +1,491 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cactus"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf034765b7d19a011c6d619e880582bf95e8186b580e6fab56589872dd87dcf5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfgrammar"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41879646bd1fbd55efdb9e2c1ac85e63856ad951e4e14b2e3086374802a13e9b"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"regex",
|
||||
"serde",
|
||||
"vob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||
|
||||
[[package]]
|
||||
name = "lrlex"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d3c956ecfc913cb0e89575c5dd74d2558ec6e12c18f23baabc2985dc48ead28"
|
||||
dependencies = [
|
||||
"cfgrammar",
|
||||
"getopts",
|
||||
"lazy_static",
|
||||
"lrpar",
|
||||
"num-traits",
|
||||
"quote",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lrpar"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3f2acdd7023dc9f0eb51b13a615477c245519f593537667116874d2c699972d"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"cactus",
|
||||
"cfgrammar",
|
||||
"filetime",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"lrtable",
|
||||
"num-traits",
|
||||
"packedvec",
|
||||
"regex",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"vergen",
|
||||
"vob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lrtable"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "310933a066b41ed437844d548c5972b02b407e63a8254d08204082a05f7a1b34"
|
||||
dependencies = [
|
||||
"cfgrammar",
|
||||
"fnv",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"sparsevec",
|
||||
"vob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packedvec"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde3c690ec20e4a2b4fb46f0289a451181eb50011a1e2acc8d85e2fde9062a45"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sparsevec"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35df5d2e580b29f3f7ec5b4ed49b0ab3acf7f3624122b3e823cafb9630f293b8"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"packedvec",
|
||||
"serde",
|
||||
"vob",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trashernet-as"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfgrammar",
|
||||
"lrlex",
|
||||
"lrpar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "8.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85e7dc29b3c54a2ea67ef4f953d5ec0c4085035c0ae2d325be1c0d2144bd9f16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustversion",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vob"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c058f4c41e71a043c67744cb76dcc1ae63ece328c1732a72489ccccc2dec23e6"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
16
sw/trashernet-as/Cargo.toml
Normal file
16
sw/trashernet-as/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "trashernet-as"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
cfgrammar = "0.13"
|
||||
lrlex = "0.13"
|
||||
lrpar = "0.13"
|
||||
|
||||
[dependencies]
|
||||
cfgrammar = "0.13"
|
||||
lrlex = "0.13"
|
||||
lrpar = "0.13"
|
15
sw/trashernet-as/build.rs
Normal file
15
sw/trashernet-as/build.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use cfgrammar::yacc::YaccKind;
|
||||
use lrlex::CTLexerBuilder;
|
||||
|
||||
fn main() {
|
||||
CTLexerBuilder::new()
|
||||
.lrpar_config(|ctp| {
|
||||
ctp.yacckind(YaccKind::Grmtools)
|
||||
.grammar_in_src_dir("calc.y")
|
||||
.unwrap()
|
||||
})
|
||||
.lexer_in_src_dir("calc.l")
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
186
sw/trashernet-as/notes.md
Normal file
186
sw/trashernet-as/notes.md
Normal file
@ -0,0 +1,186 @@
|
||||
# Trashernet Command Processor
|
||||
|
||||
Commands:
|
||||
|
||||
* MATCH (length, matches, jump), followed by match-jump-addresses if <jump>, followed by match bytes interleaved: Match incoming data against. Set <length> to zero for unconditional jump. If no match, abort reception.
|
||||
* FORWARD (length): Output <length> bytes of data on the output port
|
||||
* IGNORE (length): Ignore <length> bytes
|
||||
* OUTPUT: Output following byte on the special port
|
||||
|
||||
|
||||
Command format:
|
||||
|
||||
2b 4b 3b 1b
|
||||
[Opcode] [length] [matches] [jump]
|
||||
|
||||
|
||||
|
||||
Example program:
|
||||
|
||||
# MAC frame
|
||||
MATCH (6, 2, false) AA.BB.CC.DD.EE.FF FF.FF.FF.FF.FF.FF
|
||||
IGNORE (6)
|
||||
|
||||
# Protocol MUX
|
||||
MATCH (
|
||||
|
||||
# IPv4
|
||||
MATCH (4, 1, false) 192.168.178.1
|
||||
|
||||
|
||||
|
||||
# IDEA 2
|
||||
Okay, I need a new idea. The problem with the first is that I almost always need access to parameters from the past. E.g. ARP requires the sender MAC address for the response. In the current design, I would've already discarded that because what do I care about it for a data packet.
|
||||
|
||||
So, new approach: A packet is received into a RAM ring buffer, and then the script is started with the CRC OK.
|
||||
|
||||
New scripting language:
|
||||
|
||||
* SET_CURSOR (position): Set cursor to position in received telegram
|
||||
* MATCH[!] <address> <pattern>: Jump to address if pattern matches; else continue after pattern and reset cursor to before match (without !), or stop receiving (with !)
|
||||
* FORWARD <length>
|
||||
* OUTPUT <byte>
|
||||
* DIE
|
||||
|
||||
COMMAND formatting:
|
||||
|
||||
general : [3 bit opcode]
|
||||
MATCH : [1b deathflag] [4 bit length]
|
||||
|
||||
Example program:
|
||||
|
||||
MAC_DST: MATCH AA.BB.CC.DD.EE.FF -> ETHERTYPE
|
||||
MAC_BCAST: MATCH! FF.FF.FF.FF.FF.FF -> ETHERTYPE
|
||||
ETHERTYPE_ARP: MATCH 08.06 -> ARP
|
||||
ETHERTYPE_IP: MATCH! 08.00 -> IP
|
||||
ARP: MATCH! 00.01.08.00.06.04 -> ARP_HDR_OK
|
||||
ARP_HDR_OK: MATCH 01 -> ARP_REQUEST
|
||||
MATCH! 02 -> ARP_REPLY
|
||||
ARP_REQUEST: SET_CURSOR +10
|
||||
MATCH! AA.BB.CC.DD.EE.FF -> ARP_REQUEST_OK
|
||||
ARP_REQUEST_OK: OUTPUT 01
|
||||
SET_CURSOR -16
|
||||
FORWARD 10
|
||||
DONE
|
||||
|
||||
|
||||
This program generates an interrupt for
|
||||
* ARP (01) with 10 user bytes SHA+SPA
|
||||
|
||||
|
||||
## Now, on the sender side.
|
||||
|
||||
* TX processor runs on shared memory with receiver, possibly even shared with the receive buffer
|
||||
So that receiver can write "program memory" of transmitter, this is relevant for example for IP TX, which checks that some memory address is the target TX address.
|
||||
|
||||
|
||||
Idea for an IP TX with ARP.
|
||||
|
||||
* Destination IP is written to address in memory
|
||||
* Start IP TX program
|
||||
* ARP_CHECK: SET_CURSOR(ARP RX address buffer)
|
||||
* MATCH <destination address) -> FOUND_IP
|
||||
* WRITE_MEM <ARP TX address buffer> <destination IP>
|
||||
* SET_CURSOR(ARP TX address buffer)
|
||||
* MATCH <destination address> -> ARP_CHECK
|
||||
* !!! TBD: Assemble and send out ARP request
|
||||
* JUMP ARP_CHECK
|
||||
|
||||
* !!! TBD: Loop until MATCH
|
||||
* FOUND_IP: Assemble and send out IP frame
|
||||
|
||||
ideas for improvement:
|
||||
* have two cursors for the TX engine
|
||||
-> basically to select between static data and dynamic
|
||||
|
||||
|
||||
|
||||
|
||||
# Final (TM) Idea
|
||||
|
||||
* We build one processor. It handles both TX and RX
|
||||
* It runs code from a shared memory space with the RX buffer
|
||||
* It has multiple pointer registers (at least two)
|
||||
|
||||
Commands:
|
||||
|
||||
* Match <len, abort_flag,poll_flag> -> <dest>: Compare len characters from ptr1 and ptr2, jump to dest if all match, go to next instruction if not; if len=zero, it's an unconditional jump. `poll_flag` keeps looping here until the condition is met. `abort_flag` will abort command execution on mismatch.
|
||||
* SetPtr <ptr>, <relative/absolute>
|
||||
* Write <len>: Write <len> data from ptr2 to ptr1
|
||||
* Output <ptr, len>: Push <len> data from <ptr> to output FIFO
|
||||
* Input <ptr, len>: Read <len> bytes from input FIFO into <ptr>
|
||||
* Return: TBD: Single-level return to caller (last match?)
|
||||
* Jump <ptr1> <ptr2>: Pseudo-command, will translate to other instructions. Set location of ptr1 and ptr2 (opt.), then jump (empty match) to that address.
|
||||
|
||||
## Example to send an IP frame
|
||||
|
||||
* Write destination IP to <dest-ip>
|
||||
* Start TX IP program
|
||||
|
||||
SendUDP:
|
||||
|
||||
CheckARP:
|
||||
SetPtr ptr1, <arp-rx-ip-buffer>
|
||||
SetPtr ptr2, <dest-ip>
|
||||
Match 4 -> FOUND_ARP
|
||||
NeedARPRequest:
|
||||
Jump SendMAC, ptr1="FF:FF:FF:FF:FF:FF", ptr2="08:06"
|
||||
Jump SendARP, ptr1=<dest-ip>
|
||||
Match 4, poll -> FOUND_ARP
|
||||
FOUND_ARP:
|
||||
|
||||
|
||||
// ## SendMAC: Send MAC frame. ptr1: Destination MAC, ptr2: Ethertype
|
||||
SendMAC:
|
||||
Output ptr1, 6
|
||||
SetPtr ptr1, <my-mac>
|
||||
Output ptr1, 6
|
||||
Output ptr2, 2
|
||||
Return
|
||||
|
||||
SendARPRequest:
|
||||
Output <ARP-header> // TODO
|
||||
Output <my-mac> // SHA
|
||||
Output <my-ip> // SPA
|
||||
Output FF:FF:FF:FF:FF:FF // THA
|
||||
Output <dest-ip> // TPA
|
||||
Return
|
||||
|
||||
|
||||
|
||||
## Maybe change concept
|
||||
|
||||
Idea is: We pretty much have a template for each frame in memory. The program will assemble the data in place, and then TX the entire block
|
||||
|
||||
// Variables will be defined in order!
|
||||
|
||||
// Ethernet frame
|
||||
Def ETH-DEST-MAC:6
|
||||
Def ETH-SOURCE-MAC:6=00:DE:AD:BE:EF:00
|
||||
Def ETH-ETHERTYPE:2
|
||||
|
||||
// ARP frame
|
||||
Def ARP-HEADER:?=0001:0800:0604:0001
|
||||
Def ARP-SHA:0=Ð-SOURCE-MAC
|
||||
Def ARP-SPA:4
|
||||
Def ARP-THA:6
|
||||
Def ARP-TPA:4
|
||||
|
||||
CheckARP:
|
||||
SetPtr ptr1, <arp-rx-ip-buffer>
|
||||
SetPtr ptr2, <dest-ip>
|
||||
Match 4 -> FOUND_ARP
|
||||
NeedARPRequest:
|
||||
// Build Ethernet header
|
||||
Write Ð-DEST-MAC, "FF:FF:FF:FF:FF:FF"
|
||||
Write Ð-ETHERTYPE, "0806"
|
||||
Output Ð-DEST-MAC, 18
|
||||
// Build ARP header
|
||||
Write &ARP_TPA, <dest-ip>, 4
|
||||
Output &ARP-HEADER, 8
|
||||
Output &ARP_SHA, 6
|
||||
Output &ARP_SPA, 4
|
||||
Output &ARP-THA, 10 // +ARP-TPA
|
||||
// todo: set up match
|
||||
Match 4, poll -> FOUND_ARP
|
||||
FOUND_ARP:
|
9
sw/trashernet-as/src/calc.l
Normal file
9
sw/trashernet-as/src/calc.l
Normal file
@ -0,0 +1,9 @@
|
||||
%%
|
||||
SetPtr "SetPtr"
|
||||
[0-9]+ "NUM-DEC"
|
||||
0x[0-9a-fA-F]+ "NUM-HEX"
|
||||
0b[0-1]+ "NUM-BIN"
|
||||
[\n] "NEWLINE"
|
||||
[\t ]+ ;
|
||||
&[0-9a-zA-Z_]+ "Reference"
|
||||
[0-9a-zA-Z ] "text"
|
95
sw/trashernet-as/src/calc.y
Normal file
95
sw/trashernet-as/src/calc.y
Normal file
@ -0,0 +1,95 @@
|
||||
%start CommandSequence
|
||||
%%
|
||||
|
||||
CommandSequence -> Result <LinkedList<Result<LineOfCode, CompilerError>>, CompilerError>:
|
||||
CommandLine CommandSequence
|
||||
{
|
||||
let mut list = $2?;
|
||||
list.push_front($1);
|
||||
Ok(list)
|
||||
}
|
||||
| CommandLine
|
||||
{
|
||||
let mut list : LinkedList<Result<LineOfCode, CompilerError>> = LinkedList::new();
|
||||
list.push_front($1);
|
||||
Ok(list)
|
||||
}
|
||||
;
|
||||
|
||||
CommandLine -> Result<LineOfCode, CompilerError>:
|
||||
Command 'NEWLINE'
|
||||
{
|
||||
Ok(LineOfCode{line : 42, code: $1})
|
||||
}
|
||||
| 'NEWLINE'
|
||||
{
|
||||
Err(CompilerError {pos: $1.unwrap().span(), description: CompilerErrorType::MiscError})
|
||||
}
|
||||
;
|
||||
|
||||
/*Comment -> Result<(), ()>:
|
||||
';' 'text':
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
;*/
|
||||
|
||||
Command -> Result <Code, CompilerError>:
|
||||
'SetPtr' PointerId Symbol
|
||||
{
|
||||
Ok(Code::Instruction(InstructionType::SetPtr{which: $2?, address: $3?}))
|
||||
}
|
||||
;
|
||||
|
||||
PointerId -> Result<u16, CompilerError>:
|
||||
Literal
|
||||
{
|
||||
let num = $1?;
|
||||
if num > 0 && num <= 1 {
|
||||
Ok(num)
|
||||
} else {
|
||||
//Err(CompilerError {pos: $lexer.line_col($1.unwrap().span()), description: CompilerErrorType::MiscError})
|
||||
Ok(42) // TODO: This should raise an error, but I don't know the span anymore -.-
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
Symbol -> Result<Symbol, CompilerError>:
|
||||
Literal
|
||||
{
|
||||
Ok(Symbol::ResolvedSymbol{address: $1?})
|
||||
}
|
||||
| Reference
|
||||
{
|
||||
Ok(Symbol::UnresolvedSymbol{symbol: $1?})
|
||||
}
|
||||
;
|
||||
|
||||
Reference -> Result<String, CompilerError>:
|
||||
'Reference'
|
||||
{
|
||||
Ok($lexer.span_str($1.unwrap().span())[1..].to_string())
|
||||
}
|
||||
;
|
||||
|
||||
Literal -> Result<u16, CompilerError>:
|
||||
'NUM-HEX'
|
||||
{
|
||||
parse_number(16, $1.unwrap().span(), &$lexer.span_str($1.unwrap().span())[2..]) // TODO: Change error to include line/col etc.
|
||||
}
|
||||
| 'NUM-DEC'
|
||||
{
|
||||
parse_number(10, $1.unwrap().span(), &$lexer.span_str($1.unwrap().span()))
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
use crate::compiler_types::*;
|
||||
use std::collections::LinkedList;
|
||||
|
||||
fn parse_number(radix: u32, span: lrpar::Span, s: &str) -> Result<u16, CompilerError> {
|
||||
//Ok(u16::from_str_radix(s, radix)?)
|
||||
//u16::from_str_radix(s, radix).map_err(CompilerError{description: CompilerErrorType::MiscError})
|
||||
u16::from_str_radix(s, radix).map_err(|e| CompilerError{pos: span, description: e.into()})
|
||||
}
|
73
sw/trashernet-as/src/compiler_types.rs
Normal file
73
sw/trashernet-as/src/compiler_types.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use std::num;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Symbol {
|
||||
UnresolvedSymbol{symbol: String},
|
||||
ResolvedSymbol{address: u16}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JumpCondition {
|
||||
Always,
|
||||
OnMatch,
|
||||
OnTimer
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InstructionType {
|
||||
SetPtr{which: u16, address: Symbol},
|
||||
Output{length: Symbol},
|
||||
Match{length: Symbol},
|
||||
Write{length: Symbol},
|
||||
Timer{time: Symbol},
|
||||
Page{page: Symbol},
|
||||
Return,
|
||||
Input{length: Symbol},
|
||||
Jump{address: Symbol, condition: JumpCondition}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Code {
|
||||
Instruction(InstructionType),
|
||||
Comment(String)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LineOfCode {
|
||||
pub line: usize,
|
||||
pub code: Result<Code, CompilerError>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompilerError {
|
||||
pub pos: lrpar::Span,
|
||||
pub description: CompilerErrorType
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompilerErrorType {
|
||||
LiteralError(num::ParseIntError),
|
||||
LexerError(lrlex::DefaultLexeme),
|
||||
MiscError
|
||||
}
|
||||
|
||||
|
||||
impl From<num::ParseIntError> for CompilerErrorType {
|
||||
fn from(e: num::ParseIntError) -> Self {
|
||||
CompilerErrorType::LiteralError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lrlex::DefaultLexeme> for CompilerErrorType {
|
||||
fn from(e: lrlex::DefaultLexeme) -> Self {
|
||||
CompilerErrorType::LexerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl fmt::Debug for LineOfCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Line = {}", self.line)
|
||||
}
|
||||
}
|
||||
*/
|
25
sw/trashernet-as/src/main.rs
Normal file
25
sw/trashernet-as/src/main.rs
Normal file
@ -0,0 +1,25 @@
|
||||
mod compiler_types;
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
use lrlex::lrlex_mod;
|
||||
use lrpar::lrpar_mod;
|
||||
|
||||
lrlex_mod!("calc.l");
|
||||
lrpar_mod!("calc.y");
|
||||
|
||||
fn main() {
|
||||
let lexerdef = calc_l::lexerdef();
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let code = fs::read_to_string(&args[1]).unwrap();
|
||||
let lexer = lexerdef.lexer(&code);
|
||||
let (res, errs) = calc_y::parse(&lexer);
|
||||
for e in errs {
|
||||
println!("{}", e.pp(&lexer, &calc_y::token_epp));
|
||||
}
|
||||
match res {
|
||||
Some(r) => println!("Result: {:#?}", r),
|
||||
_ => eprintln!("Unable to evaluate expression.")
|
||||
}
|
||||
}
|
4
sw/trashernet-as/test.tas
Normal file
4
sw/trashernet-as/test.tas
Normal file
@ -0,0 +1,4 @@
|
||||
SetPtr 1 22
|
||||
SetPtr 2 0x42
|
||||
SetPtr 3 &Ref
|
||||
SetPtr 4 99999
|
@ -34,11 +34,11 @@ entity fifo is
|
||||
end entity fifo;
|
||||
|
||||
architecture rtl of fifo is
|
||||
type memory_t is array (natural range <>) of std_logic_vector(data_in'range);
|
||||
signal memory : memory_t(0 to DEPTH - 1);
|
||||
type memory_t is array (natural range <>) of std_logic_vector(data_in'range);
|
||||
signal memory : memory_t(0 to DEPTH - 1);
|
||||
|
||||
subtype memory_pointer_t is integer range 0 to DEPTH - 1;
|
||||
subtype usage_counter_t is integer range 0 to DEPTH;
|
||||
subtype usage_counter_t is integer range 0 to DEPTH;
|
||||
|
||||
signal read_pointer : memory_pointer_t;
|
||||
signal write_pointer : memory_pointer_t;
|
||||
|
@ -55,25 +55,26 @@ architecture rtl of trashernet_arp is
|
||||
signal arp_tx_reply_ack : std_logic; -- ARP reply has been latched and is being sent
|
||||
|
||||
signal arp_tx_request_rq : std_logic; -- Request to transmit an ARP request
|
||||
signal arp_tx_request_tpa : ip_addr_t; -- IP address that we want to know the MAC address of
|
||||
signal arp_tx_request_tpa : ip_addr_t; -- IP address that we want to know the MAC address of, either real target or gateway
|
||||
signal arp_tx_request_ack : std_logic; -- ARP request has been latched and is being sent
|
||||
|
||||
signal arp_rx_reply_stb : std_logic; -- An ARP reply was received (strobe)
|
||||
signal arp_rx_sha : mac_addr_t; -- The MAC address of the reply sender, valid with arp_rx_reply
|
||||
signal arp_rx_spa : ip_addr_t; -- The IP address of the reply sender, valid with arp_rx_reply
|
||||
|
||||
begin
|
||||
resolver : block
|
||||
type resolver_state_t is (IDLE, QUERY_MAC);
|
||||
signal state : resolver_state_t;
|
||||
type resolver_state_t is (IDLE, QUERY_MAC);
|
||||
signal state : resolver_state_t;
|
||||
|
||||
signal query_mac_found : std_logic;
|
||||
signal query_timeout : std_logic := '0'; -- TODO: Implement
|
||||
|
||||
signal replied_ip : ip_addr_t;
|
||||
signal replied_mac : mac_addr_t;
|
||||
begin
|
||||
|
||||
signal arp_query_stb : std_logic; -- Pipelined version of arp_in.arp_query_stb
|
||||
signal target_is_in_subnet : std_logic; -- Indicates whether the target IP is the subnet or whether to send this to the gateway
|
||||
begin
|
||||
arp_resolver_main : process(rst, clk) is
|
||||
begin
|
||||
if rst then
|
||||
@ -96,7 +97,7 @@ begin
|
||||
|
||||
case state is
|
||||
when IDLE =>
|
||||
if arp_in.arp_query_stb then
|
||||
if arp_query_stb then
|
||||
if query_mac_found then
|
||||
arp_out.arp_ok_stb <= '1';
|
||||
else
|
||||
@ -118,9 +119,21 @@ begin
|
||||
end if;
|
||||
end process arp_resolver_main;
|
||||
query_mac_found <= '1' when (arp_tx_request_tpa = replied_ip) else '0';
|
||||
target_is_in_subnet <= or((arp_in.arp_ip xor ip_config.gateway) and ip_config.subnet_mask);
|
||||
|
||||
arp_out.arp_mac <= replied_mac;
|
||||
arp_tx_request_tpa <= arp_in.arp_ip;
|
||||
|
||||
-- Pipelines the start of query so that we get some additional time to evaluate the target+netmask.
|
||||
pipeline : process (clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
arp_tx_request_tpa <= (others => x"00");
|
||||
arp_query_stb <= '0';
|
||||
elsif rising_edge(clk) then
|
||||
arp_tx_request_tpa <= arp_in.arp_ip when target_is_in_subnet else ip_config.gateway;
|
||||
arp_query_stb <= arp_in.arp_query_stb;
|
||||
end if;
|
||||
end process pipeline;
|
||||
|
||||
timeout_timer_inst : entity work.timer
|
||||
generic map(
|
||||
@ -146,19 +159,19 @@ begin
|
||||
signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1);
|
||||
|
||||
constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD;
|
||||
alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1);
|
||||
alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3);
|
||||
alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4);
|
||||
alias sr_head_plen is sr(SR_HEAD_OFFSET + 5);
|
||||
alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7);
|
||||
alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1);
|
||||
alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3);
|
||||
alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4);
|
||||
alias sr_head_plen is sr(SR_HEAD_OFFSET + 5);
|
||||
alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7);
|
||||
|
||||
constant SR_ADDRESSES_OFFSET : integer := -8;
|
||||
alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET);
|
||||
alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET);
|
||||
alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC
|
||||
alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET);
|
||||
alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET);
|
||||
alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET);
|
||||
alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC
|
||||
alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET);
|
||||
|
||||
type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE);
|
||||
type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE);
|
||||
signal state : state_t;
|
||||
signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES;
|
||||
signal shifted : std_logic;
|
||||
@ -240,7 +253,7 @@ begin
|
||||
state <= HEAD;
|
||||
end if;
|
||||
|
||||
when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
|
||||
when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
|
||||
null;
|
||||
end case;
|
||||
end if;
|
||||
@ -253,14 +266,14 @@ begin
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
signal sr : byte_vector(0 to 27);
|
||||
signal arp_reply_preload : byte_vector(sr'range);
|
||||
signal arp_request_preload : byte_vector(sr'range);
|
||||
constant BYTECOUNT_MAX : integer := sr'length + 1;
|
||||
signal bytecount : integer range 0 to BYTECOUNT_MAX;
|
||||
signal sr : byte_vector(0 to 27);
|
||||
signal arp_reply_preload : byte_vector(sr'range);
|
||||
signal arp_request_preload : byte_vector(sr'range);
|
||||
constant BYTECOUNT_MAX : integer := sr'length + 1;
|
||||
signal bytecount : integer range 0 to BYTECOUNT_MAX;
|
||||
|
||||
type state_t is (IDLE, TRANSMIT);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, TRANSMIT);
|
||||
signal state : state_t;
|
||||
begin
|
||||
|
||||
arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header
|
||||
|
@ -46,11 +46,11 @@ architecture rtl of trashernet_eth is
|
||||
signal tx_data_ack_i : std_logic;
|
||||
begin
|
||||
rx : block
|
||||
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
|
||||
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
|
||||
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
|
||||
signal mac_destination_matches : std_logic;
|
||||
signal rx_mac_header_rcv_delayed : std_logic;
|
||||
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
|
||||
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
|
||||
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
|
||||
signal mac_destination_matches : std_logic;
|
||||
signal rx_mac_header_rcv_delayed : std_logic;
|
||||
|
||||
begin
|
||||
mac_destination_matches <= '1' when --
|
||||
@ -87,22 +87,22 @@ begin
|
||||
|
||||
-- Shared fields in the Ethernet II application interface
|
||||
shared_fields : for i in ethernet_ii_out'range generate
|
||||
ethernet_ii_out(i).rx_header.mac_header <= mac_out.rx_header;
|
||||
ethernet_ii_out(i).rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_ii_out(i).rx_data_valid <= mac_out.rx_mac_valid when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_error <= mac_out.rx_mac_crc_error when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_mac_address <= mac_out.rx_header.mac_source;
|
||||
ethernet_ii_out(i).rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_ii_out(i).rx_data_valid <= mac_out.rx_mac_valid when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_error <= mac_out.rx_mac_crc_error when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0';
|
||||
end generate shared_fields;
|
||||
|
||||
-- Shared fields in the Ethernet application interface
|
||||
ethernet_i_out.rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_i_out.rx_data_valid <= mac_out.rx_mac_valid when sel = SEL_ETH_I;
|
||||
ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_header_rcv <= rx_mac_header_rcv_delayed when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_header.mac_header <= mac_out.rx_header;
|
||||
ethernet_i_out.rx_header.length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1));
|
||||
ethernet_i_out.rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_i_out.rx_data_valid <= mac_out.rx_mac_valid when sel = SEL_ETH_I;
|
||||
ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_header_rcv <= rx_mac_header_rcv_delayed when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_mac_address <= mac_out.rx_header.mac_source;
|
||||
ethernet_i_out.rx_length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1));
|
||||
|
||||
-- TX fields (need to be driven here to avoid multiple drivers (blocks drive ALL signals of a record))
|
||||
tx_fields : for i in ethernet_ii_out'range generate
|
||||
@ -112,10 +112,10 @@ begin
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, TXD);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, TXD);
|
||||
signal state : state_t;
|
||||
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
|
||||
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
|
||||
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
@ -141,10 +141,7 @@ begin
|
||||
end if;
|
||||
|
||||
when TXD =>
|
||||
if not mac_in.tx_mac_data_en then
|
||||
state <= IDLE;
|
||||
sel <= SEL_ETH_I; -- To avoid arbitration errors, always select the highest priority one by default
|
||||
end if;
|
||||
state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
|
||||
end case;
|
||||
end if;
|
||||
end process arb;
|
||||
|
@ -56,13 +56,13 @@ begin
|
||||
);
|
||||
|
||||
rx : block
|
||||
signal sr : byte_vector(0 to 3);
|
||||
alias sr_type is sr(0);
|
||||
alias sr_code is sr(1);
|
||||
alias sr_checksum is sr(2 to 3);
|
||||
signal sr : byte_vector(0 to 3);
|
||||
alias sr_type is sr(0);
|
||||
alias sr_code is sr(1);
|
||||
alias sr_checksum is sr(2 to 3);
|
||||
|
||||
type state_t is (HEADER, DATA, IGNORE);
|
||||
signal state : state_t;
|
||||
type state_t is (HEADER, DATA, IGNORE);
|
||||
signal state : state_t;
|
||||
|
||||
signal byte_count : integer range 0 to sr'length;
|
||||
|
||||
@ -117,8 +117,8 @@ begin
|
||||
signal checksum : unsigned(16 downto 0);
|
||||
signal checksum_ones : unsigned(15 downto 0);
|
||||
|
||||
type state_t is (IDLE, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
|
||||
signal sr : byte_vector(0 to 3);
|
||||
signal byte_count : integer range 0 to sr'subtype'high;
|
||||
@ -140,8 +140,8 @@ begin
|
||||
case state is
|
||||
when IDLE =>
|
||||
if tx_response then
|
||||
ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_header.ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
|
||||
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_header.length;
|
||||
ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
|
||||
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length;
|
||||
sr <= byte_vector'(
|
||||
x"00",
|
||||
x"00",
|
||||
|
@ -44,28 +44,27 @@ begin
|
||||
constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields
|
||||
|
||||
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
|
||||
alias sr_version is sr(0)(7 downto 4);
|
||||
alias sr_ihl is sr(0)(3 downto 0);
|
||||
alias sr_dscp is sr(1)(7 downto 2);
|
||||
alias sr_ecn is sr(1)(1 downto 0);
|
||||
alias sr_total_length is sr(2 to 3);
|
||||
alias sr_identification is sr(4 to 5);
|
||||
alias sr_flags is sr(6)(7 downto 5);
|
||||
alias sr_version is sr(0)(7 downto 4);
|
||||
alias sr_ihl is sr(0)(3 downto 0);
|
||||
alias sr_dscp is sr(1)(7 downto 2);
|
||||
alias sr_ecn is sr(1)(1 downto 0);
|
||||
alias sr_total_length is sr(2 to 3);
|
||||
alias sr_identification is sr(4 to 5);
|
||||
alias sr_flags is sr(6)(7 downto 5);
|
||||
signal sr_fragment_offset : std_logic_vector(12 downto 0);
|
||||
alias sr_ttl is sr(8);
|
||||
alias sr_protocol is sr(9);
|
||||
alias sr_header_cks is sr(10 to 11);
|
||||
alias sr_source_ip is sr(12 to 15);
|
||||
alias sr_destination_ip is sr(16 to 19);
|
||||
alias sr_ttl is sr(8);
|
||||
alias sr_protocol is sr(9);
|
||||
alias sr_header_cks is sr(10 to 11);
|
||||
alias sr_source_ip is sr(12 to 15);
|
||||
alias sr_destination_ip is sr(16 to 19);
|
||||
|
||||
signal header_ok : std_logic;
|
||||
signal selected : std_logic;
|
||||
|
||||
signal bytecount : integer range 0 to 65535;
|
||||
signal block_done : std_logic;
|
||||
|
||||
type state_t is (HEADER, OPT, PAYLOAD, DONE);
|
||||
signal state : state_t;
|
||||
type state_t is (HEADER, OPT, PAYLOAD, DONE);
|
||||
signal state : state_t;
|
||||
begin
|
||||
sr_fragment_offset <= sr(6)(4 downto 0) & sr(7);
|
||||
|
||||
@ -78,18 +77,24 @@ begin
|
||||
rx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= DONE;
|
||||
state <= HEADER;
|
||||
ipv4_out.rx_ok_stb <= '0';
|
||||
ipv4_out.rx_error_stb <= '0';
|
||||
ipv4_out.rx_header_rcv <= '0';
|
||||
bytecount <= BYTECOUNT_HEADER;
|
||||
selected <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
ipv4_out.rx_ok_stb <= '0';
|
||||
ipv4_out.rx_error_stb <= '0';
|
||||
ipv4_out.rx_header_rcv <= '0';
|
||||
|
||||
if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
|
||||
state <= HEADER;
|
||||
bytecount <= BYTECOUNT_HEADER;
|
||||
|
||||
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
|
||||
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
|
||||
end if;
|
||||
if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
|
||||
if state = HEADER then
|
||||
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data;
|
||||
@ -103,10 +108,7 @@ begin
|
||||
bytecount <= to_integer(unsigned(sr_ihl) - 5) * 4; -- five 32-bit words is the header itself
|
||||
if header_ok then
|
||||
state <= OPT;
|
||||
selected <= '1';
|
||||
ipv4_out.rx_header_rcv <= '1';
|
||||
else
|
||||
state <= DONE;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
@ -124,30 +126,20 @@ begin
|
||||
when DONE => -- @suppress "Dead state 'DONE'": Outgoing state transition is outside of case statement
|
||||
null; -- We just wait here until the MAC gives us a CRC OK/error
|
||||
end case;
|
||||
if selected and (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
|
||||
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
|
||||
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
|
||||
end if;
|
||||
if (ethernet_ii_out.rx_header_rcv) then
|
||||
state <= HEADER;
|
||||
bytecount <= BYTECOUNT_HEADER;
|
||||
selected <= '0';
|
||||
end if;
|
||||
end if;
|
||||
end process rx_fsm;
|
||||
|
||||
ipv4_out.rx_data <= ethernet_ii_out.rx_data;
|
||||
ipv4_out.rx_data_valid <= ethernet_ii_out.rx_data_valid when state = PAYLOAD else '0';
|
||||
ipv4_out.rx_header.eth_header <= ethernet_ii_out.rx_header;
|
||||
ipv4_out.rx_header.ip_address <= sr_source_ip;
|
||||
ipv4_out.rx_header.protocol <= sr_protocol;
|
||||
ipv4_out.rx_header.length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
|
||||
ipv4_out.rx_data <= ethernet_ii_out.rx_data;
|
||||
ipv4_out.rx_data_valid <= ethernet_ii_out.rx_data_valid when state = PAYLOAD else '0';
|
||||
ipv4_out.rx_ip_address <= sr_source_ip;
|
||||
ipv4_out.rx_protocol <= sr_protocol;
|
||||
ipv4_out.rx_length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
|
||||
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
|
||||
signal full_length : ipv4_length;
|
||||
|
||||
@ -182,13 +174,11 @@ begin
|
||||
ipv4_out.tx_data_ack <= '0';
|
||||
arp_in.arp_query_stb <= '0';
|
||||
ipv4_out.tx_err_stb <= '0';
|
||||
ipv4_out.tx_ok_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
arp_in.arp_query_stb <= '0';
|
||||
ipv4_out.tx_data_ack <= '0';
|
||||
ipv4_out.tx_err_stb <= '0';
|
||||
ipv4_out.tx_ok_stb <= '0';
|
||||
|
||||
if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) then
|
||||
if byte_cnt > 0 then
|
||||
@ -202,12 +192,6 @@ begin
|
||||
if ipv4_in.tx_en then
|
||||
arp_in.arp_query_stb <= '1';
|
||||
state <= ARP;
|
||||
|
||||
if or((to_std_logic_vector(ipv4_config.gateway) xor to_std_logic_vector(arp_in.arp_ip)) and to_std_logic_vector(ipv4_config.subnet_mask)) then
|
||||
arp_in.arp_ip <= ipv4_config.gateway;
|
||||
else
|
||||
arp_in.arp_ip <= ipv4_in.tx_ip_address;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when ARP =>
|
||||
@ -248,8 +232,7 @@ begin
|
||||
sr(sr'low) <= ipv4_in.tx_data;
|
||||
ipv4_out.tx_data_ack <= '1';
|
||||
if not ipv4_in.tx_en then
|
||||
state <= IDLE;
|
||||
ipv4_out.tx_ok_stb <= '1';
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end if;
|
||||
end case;
|
||||
@ -258,6 +241,9 @@ begin
|
||||
|
||||
ethernet_ii_in.tx_data <= sr(sr'low);
|
||||
ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0';
|
||||
|
||||
arp_in.arp_ip <= ipv4_in.tx_ip_address;
|
||||
|
||||
end block tx;
|
||||
|
||||
end architecture rtl;
|
||||
|
@ -57,7 +57,7 @@ begin
|
||||
if ipv4_out.rx_header_rcv then
|
||||
rx_sel <= SEL_PROTOCOL_NONE;
|
||||
for i in IPV4_PROTOCOLS'range loop
|
||||
if (ipv4_out.rx_header.protocol = IPV4_PROTOCOLS(i)) then
|
||||
if (ipv4_out.rx_protocol = IPV4_PROTOCOLS(i)) then
|
||||
rx_sel <= i;
|
||||
end if;
|
||||
end loop;
|
||||
@ -67,7 +67,8 @@ begin
|
||||
|
||||
mux : for i in ipv4_protocol_out'range generate
|
||||
ipv4_protocol_out(i).rx_data <= ipv4_out.rx_data;
|
||||
ipv4_protocol_out(i).rx_header <= ipv4_out.rx_header;
|
||||
ipv4_protocol_out(i).rx_ip_address <= ipv4_out.rx_ip_address;
|
||||
ipv4_protocol_out(i).rx_length <= ipv4_out.rx_length;
|
||||
ipv4_protocol_out(i).rx_data_valid <= ipv4_out.rx_data_valid when rx_sel = i else '0';
|
||||
ipv4_protocol_out(i).rx_error_stb <= ipv4_out.rx_error_stb when rx_sel = i else '0';
|
||||
ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
|
||||
@ -82,8 +83,8 @@ begin
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, TXD, WAITDONE);
|
||||
signal state : state_t;
|
||||
type state_t is (IDLE, TXD);
|
||||
signal state : state_t;
|
||||
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
@ -104,13 +105,7 @@ begin
|
||||
end loop;
|
||||
|
||||
when TXD =>
|
||||
state <= WAITDONE when (not ipv4_in.tx_en);
|
||||
|
||||
when WAITDONE =>
|
||||
if ipv4_out.tx_err_stb or ipv4_out.tx_ok_stb or ipv4_out.tx_data_ack then
|
||||
state <= IDLE;
|
||||
tx_sel <= ipv4_protocol_in'left; -- To avoid arbitration errors, always select the highest priority one by default
|
||||
end if;
|
||||
state <= IDLE when (not ipv4_in.tx_en);
|
||||
end case;
|
||||
end if;
|
||||
end process arb;
|
||||
|
@ -41,10 +41,10 @@ begin
|
||||
|
||||
constant HEAD_LENGTH : integer := mac_out.rx_header.mac_destination'length + mac_out.rx_header.mac_source'length + mac_out.rx_header.mac_ethertype'length;
|
||||
constant CRC_LENGTH : integer := 4;
|
||||
signal state : state_t;
|
||||
signal sr_head : byte_vector(0 to HEAD_LENGTH - 1);
|
||||
signal state : state_t;
|
||||
signal sr_head : byte_vector(0 to HEAD_LENGTH - 1);
|
||||
constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1;
|
||||
signal byte_count : integer range 0 to BYTE_COUNT_MAX;
|
||||
signal byte_count : integer range 0 to BYTE_COUNT_MAX;
|
||||
|
||||
signal sr_payload : byte_vector(0 to 4);
|
||||
|
||||
@ -102,7 +102,7 @@ begin
|
||||
byte_count <= byte_count + 1;
|
||||
end if;
|
||||
end if;
|
||||
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
|
||||
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
|
||||
if phy_out.rx_data_valid then
|
||||
sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data;
|
||||
if byte_count = CRC_LENGTH then
|
||||
@ -136,14 +136,14 @@ begin
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC);
|
||||
signal tx_state : tx_state_t;
|
||||
type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC);
|
||||
signal tx_state : tx_state_t;
|
||||
|
||||
signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1);
|
||||
signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1);
|
||||
constant SR_CNT_MAX : integer := sr'high;
|
||||
signal sr_cnt : integer range 0 to SR_CNT_MAX;
|
||||
signal sr_cnt : integer range 0 to SR_CNT_MAX;
|
||||
constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4; -- Minus four because the CRC is *after* the PAD
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
|
||||
signal crc : std_logic_vector(ETH_POLYNOMIAL'range);
|
||||
signal crc_clear : std_logic;
|
||||
|
@ -44,7 +44,7 @@ begin
|
||||
|
||||
common : block
|
||||
constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1;
|
||||
signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX;
|
||||
signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX;
|
||||
begin
|
||||
mstick : process(clk, rst) is
|
||||
begin
|
||||
@ -80,7 +80,7 @@ begin
|
||||
|
||||
-- NLP supervision
|
||||
constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms)
|
||||
signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX;
|
||||
signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX;
|
||||
begin
|
||||
-- Synchronize RX input
|
||||
synchronizer_rxp_inst : entity work.synchronizer
|
||||
@ -111,26 +111,26 @@ begin
|
||||
|
||||
demanchestizer : block
|
||||
-- Transition detector
|
||||
constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1;
|
||||
constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1;
|
||||
constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5;
|
||||
constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1;
|
||||
signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT;
|
||||
type transition_duration_t is (SHORT, LONG);
|
||||
signal transition_duration : transition_duration_t;
|
||||
signal transition_stb : std_logic;
|
||||
signal transition_activity : std_logic;
|
||||
constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1;
|
||||
constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1;
|
||||
constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5;
|
||||
constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1;
|
||||
signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT;
|
||||
type transition_duration_t is (SHORT, LONG);
|
||||
signal transition_duration : transition_duration_t;
|
||||
signal transition_stb : std_logic;
|
||||
signal transition_activity : std_logic;
|
||||
|
||||
-- Transition analysis
|
||||
signal last_transition : transition_duration_t;
|
||||
type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR);
|
||||
type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR);
|
||||
signal bit_ev : bit_ev_t;
|
||||
|
||||
-- Bit recovery
|
||||
type demanchestization_state_t is (SYNC, DATA, ERROR);
|
||||
signal demanchestization_state : demanchestization_state_t;
|
||||
type demanchestization_state_t is (SYNC, DATA, ERROR);
|
||||
signal demanchestization_state : demanchestization_state_t;
|
||||
constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes
|
||||
signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
|
||||
signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
|
||||
begin
|
||||
-- Detects spacing of transitions
|
||||
transition_detector : process(clk, rst) is
|
||||
@ -253,7 +253,6 @@ begin
|
||||
begin
|
||||
if rst then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
@ -297,25 +296,22 @@ begin
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
transmitter : block
|
||||
constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
|
||||
constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
|
||||
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
|
||||
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
|
||||
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
|
||||
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1;
|
||||
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
|
||||
|
||||
type tx_state_t is (IDLE, NLP, TX, IPG);
|
||||
signal tx_state : tx_state_t;
|
||||
signal sr : std_logic_vector(phy_in.tx_data'range);
|
||||
signal bit_stage : std_logic;
|
||||
type tx_state_t is (IDLE, NLP, TX, IPG);
|
||||
signal tx_state : tx_state_t;
|
||||
signal sr : std_logic_vector(phy_in.tx_data'range);
|
||||
signal bit_stage : std_logic;
|
||||
|
||||
constant BIT_CNT_MAX_IPG : integer := 96;
|
||||
constant BIT_CNT_MAX_DATA : integer := sr'length - 1;
|
||||
signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA);
|
||||
signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA);
|
||||
constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms)
|
||||
signal nlp_cnt : integer range 0 to NLP_CNT_MAX;
|
||||
signal nlp_cnt : integer range 0 to NLP_CNT_MAX;
|
||||
|
||||
type tx_mode_t is (OFF, NLP, ACTIVE);
|
||||
signal tx_mode : tx_mode_t;
|
||||
type tx_mode_t is (OFF, NLP, ACTIVE);
|
||||
signal tx_mode : tx_mode_t;
|
||||
begin
|
||||
tx_main : process(clk, rst) is
|
||||
procedure transmit_byte is
|
||||
@ -371,9 +367,6 @@ begin
|
||||
|
||||
if tx_stb_cnt = 0 then
|
||||
tx_stb_cnt <= TX_STB_CNT_MAX;
|
||||
if TX_STB_SKIP_SECOND and bit_stage = '1' then
|
||||
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
|
||||
end if;
|
||||
else
|
||||
tx_stb_cnt <= tx_stb_cnt - 1;
|
||||
end if;
|
||||
|
@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
|
||||
signal phy_phy_in : phy_in_t;
|
||||
|
||||
-- Helper signals
|
||||
signal rx_data_i : byte;
|
||||
signal rx_data_valid_i : std_logic;
|
||||
|
||||
begin
|
||||
assert F_CLK_PHY > 2 * F_CLK report "F_CLK_PHY must be at least 2x F_CLK" severity failure;
|
||||
|
||||
-- -------------------------------------------------------------------------
|
||||
-- Drives: PHY clock domain
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
-- Reset synchronizer for PHY
|
||||
rstsync : process(phy_clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
@ -61,7 +61,6 @@ begin
|
||||
end if;
|
||||
end process rstsync;
|
||||
|
||||
-- Operate Trashernet in PHY clock domain
|
||||
trashernet_phy_inst : entity work.trashernet_phy
|
||||
generic map(
|
||||
F_CLK => F_CLK_PHY
|
||||
@ -76,22 +75,6 @@ begin
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
-- Latch data in PHY clock domain when valid is strobed
|
||||
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
|
||||
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
|
||||
-- Therefore, we need to latch it here.
|
||||
rxdff : process(phy_clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
rx_data_i <= (others => '0');
|
||||
|
||||
elsif rising_edge(phy_clk) then
|
||||
if phy_phy_out.rx_data_valid then
|
||||
rx_data_i <= phy_phy_out.rx_data;
|
||||
end if;
|
||||
end if;
|
||||
end process rxdff;
|
||||
|
||||
synchronizer_txen_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 5
|
||||
@ -115,10 +98,18 @@ begin
|
||||
a_in => phy_phy_out.rx_data_valid,
|
||||
b_clk => clk,
|
||||
b_rst => rst,
|
||||
b_out => phy_out.rx_data_valid
|
||||
b_out => rx_data_valid_i
|
||||
);
|
||||
|
||||
phy_out.rx_data <= rx_data_i; -- No need to synchronize in new clock domain as latched data has been stable for a while thanks to the delay in the _valid synchronizer
|
||||
rxdvff : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
elsif rising_edge(clk) then
|
||||
phy_out.rx_data_valid <= rx_data_valid_i;
|
||||
phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer
|
||||
end if;
|
||||
end process rxdvff;
|
||||
|
||||
cdc_strobe_rxer_inst : entity work.cdc_strobe
|
||||
port map(
|
||||
|
@ -13,70 +13,27 @@ use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
package trashernet_pkg is
|
||||
-- ----------------
|
||||
-- Type definitions
|
||||
-- ----------------
|
||||
|
||||
-- General types
|
||||
subtype byte is std_logic_vector(7 downto 0);
|
||||
type byte_vector is array (natural range <>) of byte;
|
||||
subtype byte is std_logic_vector(7 downto 0);
|
||||
type byte_vector is array (natural range <>) of byte;
|
||||
|
||||
-- MAC specific types
|
||||
subtype mac_addr_t is byte_vector(0 to 5);
|
||||
subtype mac_addr_t is byte_vector(0 to 5);
|
||||
constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF");
|
||||
function mac_to_string(constant mac_address : in mac_addr_t) return string;
|
||||
subtype ethertype_t is byte_vector(0 to 1);
|
||||
subtype ethertype_t is byte_vector(0 to 1);
|
||||
|
||||
type mac_header_t is record
|
||||
type mac_header_fields is record
|
||||
mac_destination : mac_addr_t; -- Destination MAC address
|
||||
mac_source : mac_addr_t; -- Source MAC address
|
||||
mac_ethertype : ethertype_t; -- Ethertype or length
|
||||
end record mac_header_t;
|
||||
|
||||
-- Ethernet specific types
|
||||
type ethernet_i_header_t is record
|
||||
mac_header : mac_header_t; -- MAC layer header
|
||||
length : unsigned(15 downto 0); -- RX payload length in bytes
|
||||
end record;
|
||||
|
||||
type ethernet_ii_header_t is record
|
||||
mac_header : mac_header_t; -- MAC layer header
|
||||
end record;
|
||||
end record mac_header_fields;
|
||||
|
||||
-- IP specific types
|
||||
subtype ip_addr_t is byte_vector(0 to 3);
|
||||
function ip_to_string(constant ip_address : in ip_addr_t) return string;
|
||||
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector;
|
||||
|
||||
subtype ipv4_protocol is byte;
|
||||
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
|
||||
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
|
||||
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
|
||||
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
|
||||
subtype ipv4_length is unsigned(15 downto 0);
|
||||
|
||||
type ipv4_header_t is record
|
||||
ip_address : ip_addr_t; -- Source IP address
|
||||
protocol : ipv4_protocol; -- Transport Protocol
|
||||
length : ipv4_length; -- Telegram length (excluding header)
|
||||
eth_header : ethernet_ii_header_t; -- Eth II header
|
||||
end record ipv4_header_t;
|
||||
|
||||
-- UDP specific types
|
||||
subtype udp_port_t is unsigned(15 downto 0);
|
||||
subtype udp_length_t is unsigned(15 downto 0);
|
||||
type udp_header_t is record
|
||||
source_port : udp_port_t; -- UDP source port
|
||||
destination_port : udp_port_t; -- UDP destination port
|
||||
length : udp_length_t; -- UDP length
|
||||
ipv4_header : ipv4_header_t; -- IPv4 header
|
||||
end record;
|
||||
|
||||
-- ------------------------
|
||||
-- Configuration interfaces
|
||||
-- ------------------------
|
||||
|
||||
-- System configuration
|
||||
-- Configuration interface
|
||||
type configuration_t is record
|
||||
mac_address : mac_addr_t; -- MAC address of this node
|
||||
end record configuration_t;
|
||||
@ -86,19 +43,6 @@ package trashernet_pkg is
|
||||
subnet_mask : ip_addr_t; -- Subnet mask of this node
|
||||
end record ipv4_configuration_t;
|
||||
|
||||
-- MAC ETH interface
|
||||
type ethernet_ii_protocol_t is record
|
||||
ethertype : ethertype_t;
|
||||
end record;
|
||||
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
|
||||
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
|
||||
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
|
||||
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
|
||||
|
||||
-- ----------------------
|
||||
-- Application interfaces
|
||||
-- ----------------------
|
||||
|
||||
-- PHY interface
|
||||
type phy_in_t is record
|
||||
tx_data : byte; -- TX Data
|
||||
@ -118,12 +62,12 @@ package trashernet_pkg is
|
||||
|
||||
-- MAC interface
|
||||
type mac_in_t is record
|
||||
tx_header : mac_header_t; -- TX MAC Header Data
|
||||
tx_header : mac_header_fields; -- TX MAC Header Data
|
||||
tx_mac_data : byte; -- Payload
|
||||
tx_mac_data_en : std_logic; -- Start (and keep) transmitting a frame
|
||||
end record mac_in_t;
|
||||
type mac_out_t is record
|
||||
rx_header : mac_header_t; -- RX MAC Header Data
|
||||
rx_header : mac_header_fields; -- RX MAC Header Data
|
||||
rx_mac_header_rcv : std_logic; -- `rx_mac` header have been received and are valid
|
||||
|
||||
rx_mac_data : byte; -- Ethernet data (after Ethertype)
|
||||
@ -135,18 +79,25 @@ package trashernet_pkg is
|
||||
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
end record mac_out_t;
|
||||
|
||||
-- Ethernet I interface
|
||||
-- MAC ETH interface
|
||||
type ethernet_ii_protocol_t is record
|
||||
ethertype : ethertype_t;
|
||||
end record;
|
||||
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
|
||||
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
|
||||
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
|
||||
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
|
||||
|
||||
type ethernet_i_out_t is record
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
rx_mac_address : mac_addr_t; -- Source MAC address
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
|
||||
rx_header : ethernet_i_header_t;
|
||||
rx_header_rcv : std_logic; -- Start of reception, `header` is valid
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_length : unsigned(15 downto 0); -- RX payload length in bytes
|
||||
rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
end record;
|
||||
type ethernet_i_in_t is record
|
||||
tx_mac_address : mac_addr_t; -- Destination MAC address
|
||||
@ -156,27 +107,23 @@ package trashernet_pkg is
|
||||
end record;
|
||||
constant ETHERNET_I_IN_UNUSED : ethernet_i_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", tx_length => x"0000", others => '0'); -- TODO
|
||||
|
||||
-- Ethernet II interface
|
||||
type ethernet_ii_out_t is record
|
||||
rx_header : ethernet_ii_header_t;
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
rx_mac_address : mac_addr_t; -- Source MAC address
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
end record;
|
||||
type ethernet_ii_in_t is record
|
||||
tx_mac_address : mac_addr_t; -- Destination MAC address
|
||||
tx_data : byte; -- TX data
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
end record;
|
||||
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t;
|
||||
type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t;
|
||||
constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
|
||||
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t;
|
||||
type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t;
|
||||
constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
|
||||
|
||||
-- ARP interface
|
||||
type arp_out_t is record
|
||||
@ -190,15 +137,23 @@ package trashernet_pkg is
|
||||
end record arp_in_t;
|
||||
|
||||
-- IPv4 interface
|
||||
type ipv4_out_t is record
|
||||
rx_header : ipv4_header_t;
|
||||
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
|
||||
subtype ipv4_protocol is byte;
|
||||
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
|
||||
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
|
||||
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
|
||||
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
|
||||
subtype ipv4_length is unsigned(15 downto 0);
|
||||
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
type ipv4_out_t is record
|
||||
rx_ip_address : ip_addr_t; -- Source IP address
|
||||
rx_protocol : ipv4_protocol; -- Transport Protocol
|
||||
rx_length : ipv4_length; -- Telegram length (excluding header)
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
@ -213,14 +168,14 @@ package trashernet_pkg is
|
||||
end record ipv4_in_t;
|
||||
|
||||
type ipv4_protocol_out_t is record
|
||||
rx_header : ipv4_header_t;
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
rx_ip_address : ip_addr_t; -- Source IP address
|
||||
rx_length : ipv4_length; -- Telegram length (excluding header)
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
@ -234,68 +189,9 @@ package trashernet_pkg is
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
end record ipv4_protocol_in_t;
|
||||
type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t;
|
||||
type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t;
|
||||
type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t;
|
||||
|
||||
-- UDP interface
|
||||
type udp_out_t is record
|
||||
rx_data : byte; -- RX Data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
|
||||
rx_header : udp_header_t; -- UDP header
|
||||
rx_header_rcv : std_logic; -- Start of reception, `rx_header` valid
|
||||
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
tx_err_stb : std_logic; -- Transmission failed
|
||||
end record udp_out_t;
|
||||
type udp_in_t is record
|
||||
tx_ip_address : ip_addr_t; -- Destination IP address
|
||||
tx_source_port : udp_port_t; -- UDP source port
|
||||
tx_destination_port : udp_port_t; -- UDP destination port
|
||||
tx_length : udp_length_t; -- UDP length
|
||||
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
tx_data : byte;
|
||||
end record udp_in_t;
|
||||
|
||||
type udpprot_rx_out_t is record
|
||||
rx_data : byte; -- RX Data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
|
||||
rx_header : udp_header_t; -- UDP header
|
||||
rx_header_rcv : std_logic; -- Start of reception, `rx_header` valid
|
||||
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
end record udpprot_rx_out_t;
|
||||
type udpprot_rx_in_t is record
|
||||
port_bind : udp_port_t;
|
||||
end record udpprot_rx_in_t;
|
||||
type udpprot_tx_out_t is record
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
tx_err_stb : std_logic; -- Transmission failed
|
||||
end record udpprot_tx_out_t;
|
||||
type udpprot_tx_in_t is record
|
||||
tx_ip_address : ip_addr_t; -- Destination IP address
|
||||
tx_source_port : udp_port_t; -- UDP source port
|
||||
tx_destination_port : udp_port_t; -- UDP destination port
|
||||
tx_length : udp_length_t; -- UDP length
|
||||
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
tx_data : byte;
|
||||
end record udpprot_tx_in_t;
|
||||
type udpprot_tx_out_vector is array (natural range <>) of udpprot_tx_out_t;
|
||||
type udpprot_tx_in_vector is array (natural range <>) of udpprot_tx_in_t;
|
||||
type udpprot_rx_out_vector is array (natural range <>) of udpprot_rx_out_t;
|
||||
type udpprot_rx_in_vector is array (natural range <>) of udpprot_rx_in_t;
|
||||
|
||||
-- ------------------------
|
||||
-- General helper functions
|
||||
-- ------------------------
|
||||
function to_std_logic(constant bool : boolean) return std_logic;
|
||||
end package trashernet_pkg;
|
||||
|
||||
@ -322,11 +218,6 @@ package body trashernet_pkg is
|
||||
;
|
||||
end function ip_to_string;
|
||||
|
||||
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector is
|
||||
begin
|
||||
return ip_address(0) & ip_address(1) & ip_address(2) & ip_address(3);
|
||||
end function to_std_logic_vector;
|
||||
|
||||
function to_std_logic(constant bool : boolean) return std_logic is
|
||||
variable ret : std_logic;
|
||||
begin
|
||||
|
@ -1,304 +0,0 @@
|
||||
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_rmii.vhd : Ethernet OSI Layer 1, Physical
|
||||
-- Implements interface to an RMII PHY.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
use ieee.math_real.all;
|
||||
|
||||
use work.trashernet_pkg.all;
|
||||
|
||||
entity trashernet_rmii is
|
||||
generic(
|
||||
SYSCLK_IS_REFCLK : boolean := false -- Do not generate synchronizers between the RMII and system clock domains
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock (must not be slower than rmii_ref_clk)
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- PHY application interface
|
||||
phy_out : out phy_out_t; -- PHY application IF (out)
|
||||
phy_in : in phy_in_t; -- PHY application IF (in)
|
||||
|
||||
-- RMII physical signals
|
||||
rmii_ref_clk : in std_logic; -- Synchronous clock reference for receive, transmit and control interface
|
||||
rmii_crs_dv : in std_logic; -- Carrier Sense/Receive Data Valid
|
||||
rmii_rxd : in std_logic_vector(1 downto 0); -- Receive Data
|
||||
rmii_tx_en : out std_logic; -- Transmit Enable
|
||||
rmii_txd : out std_logic_vector(1 downto 0) -- Transmit Data
|
||||
);
|
||||
end entity trashernet_rmii;
|
||||
|
||||
architecture rtl of trashernet_rmii is
|
||||
signal rmii_rst : std_logic;
|
||||
signal rmii_crs_dv_sync : std_logic;
|
||||
begin
|
||||
synchronizer_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 2
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
data_in => rmii_crs_dv,
|
||||
data_out => rmii_crs_dv_sync
|
||||
);
|
||||
|
||||
rmii_reset_gen : if SYSCLK_IS_REFCLK generate
|
||||
rmii_rst <= rst;
|
||||
|
||||
else generate
|
||||
rmii_reset_gen_p : process(rmii_ref_clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
rmii_rst <= '1';
|
||||
elsif rising_edge(rmii_ref_clk) then
|
||||
rmii_rst <= '0';
|
||||
end if;
|
||||
end process rmii_reset_gen_p;
|
||||
end generate rmii_reset_gen;
|
||||
|
||||
receive : block
|
||||
type state_t is (IDLE, DATA);
|
||||
signal state : state_t;
|
||||
signal sr : byte;
|
||||
signal dibit_cnt : integer range 0 to 3;
|
||||
|
||||
signal temp_rx_data : byte;
|
||||
signal temp_rx_active : std_logic;
|
||||
signal temp_rx_data_valid : std_logic;
|
||||
|
||||
begin
|
||||
rxp : process(rmii_rst, rmii_ref_clk) is
|
||||
begin
|
||||
if rmii_rst then
|
||||
state <= IDLE;
|
||||
|
||||
elsif rising_edge(rmii_ref_clk) then
|
||||
-- Shift in data bytes
|
||||
sr <= rmii_rxd & sr(sr'high downto 2);
|
||||
if dibit_cnt = 3 then
|
||||
dibit_cnt <= 0;
|
||||
else
|
||||
dibit_cnt <= dibit_cnt + 1;
|
||||
end if;
|
||||
|
||||
-- Sync header
|
||||
case state is
|
||||
when IDLE =>
|
||||
if sr = x"D5" then -- Sync header
|
||||
state <= DATA;
|
||||
dibit_cnt <= 1;
|
||||
end if;
|
||||
|
||||
when DATA =>
|
||||
null;
|
||||
end case;
|
||||
|
||||
-- Stop receiving whenever CRSDV disappears
|
||||
if rmii_crs_dv_sync = '0' then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end if;
|
||||
end process rxp;
|
||||
|
||||
temp_rx_data <= sr;
|
||||
temp_rx_active <= '1' when (state = DATA) else '0'; -- TODO: We might want to delay this by one cycle
|
||||
temp_rx_data_valid <= '1' when (state = DATA) and (dibit_cnt = 0) else '0';
|
||||
|
||||
phy_out.rx_error <= '0'; -- We don't implement RXER
|
||||
phy_out.carrier_detect <= '1'; -- TODO: Not yet implemented.
|
||||
|
||||
cdc_or_register : if SYSCLK_IS_REFCLK generate
|
||||
register_out : process(rmii_rst, rmii_ref_clk) is
|
||||
begin
|
||||
if rmii_rst then
|
||||
phy_out.rx_data <= (others => '0');
|
||||
phy_out.rx_active <= '0';
|
||||
phy_out.rx_data_valid <= '0';
|
||||
|
||||
elsif rising_edge(rmii_ref_clk) then
|
||||
phy_out.rx_data <= temp_rx_data;
|
||||
phy_out.rx_active <= temp_rx_active;
|
||||
phy_out.rx_data_valid <= temp_rx_data_valid;
|
||||
end if;
|
||||
end process register_out;
|
||||
|
||||
else generate
|
||||
-- CDC
|
||||
synchronizer_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 4
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
data_in => temp_rx_active,
|
||||
data_out => phy_out.rx_active
|
||||
);
|
||||
|
||||
cdc_strobe_inst : entity work.cdc_strobe
|
||||
generic map(
|
||||
SYNCHRONIZERS => 2
|
||||
)
|
||||
port map(
|
||||
a_clk => rmii_ref_clk,
|
||||
a_rst => rmii_rst,
|
||||
a_in => temp_rx_data_valid,
|
||||
b_clk => clk,
|
||||
b_rst => rst,
|
||||
b_out => phy_out.rx_data_valid
|
||||
);
|
||||
|
||||
-- Not a synchronizer, just a byte latch in the rmii_ref_clk domain
|
||||
rx_data_reg : process(rmii_ref_clk) is
|
||||
begin
|
||||
if rising_edge(rmii_ref_clk) then
|
||||
if temp_rx_data_valid then
|
||||
phy_out.rx_data <= temp_rx_data; -- TODO: We are assuming that the system clock domains runs at least x times faster than the PHY clock domain
|
||||
end if;
|
||||
end if;
|
||||
end process rx_data_reg;
|
||||
|
||||
end generate cdc_or_register;
|
||||
|
||||
end block receive;
|
||||
|
||||
transmitter : block
|
||||
constant SYNC_HEADER_SIZE_BYTES : natural := 8 - 1; -- Sync header 0x55 bytes ()
|
||||
constant IPG_SIZE_BYTES : natural := 96 / 8;
|
||||
|
||||
signal temp_tx_data : byte;
|
||||
signal temp_tx_data_en : std_logic;
|
||||
signal temp_tx_data_ack : std_logic;
|
||||
signal temp_tx_active : std_logic;
|
||||
|
||||
type state_t is (IDLE, DATA, IPG);
|
||||
signal state : state_t;
|
||||
signal sr : byte;
|
||||
signal dibit_cnt : integer range 0 to 3;
|
||||
signal byte_cnt : integer range 0 to maximum(SYNC_HEADER_SIZE_BYTES, IPG_SIZE_BYTES) - 1;
|
||||
signal byte_done : std_logic;
|
||||
signal block_done : std_logic;
|
||||
|
||||
begin
|
||||
cdc_or_register : if SYSCLK_IS_REFCLK generate
|
||||
temp_tx_data_en <= phy_in.tx_data_en;
|
||||
temp_tx_data <= phy_in.tx_data;
|
||||
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
phy_out_reg : process(rst, clk) is
|
||||
begin
|
||||
if rst then
|
||||
phy_out.tx_data_ack <= '0';
|
||||
phy_out.tx_active <= '0';
|
||||
elsif rising_edge(clk) then
|
||||
phy_out.tx_data_ack <= temp_tx_data_ack;
|
||||
phy_out.tx_active <= temp_tx_active;
|
||||
end if;
|
||||
end process phy_out_reg;
|
||||
|
||||
else generate
|
||||
synchronizer_txdv_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 2
|
||||
)
|
||||
port map(
|
||||
clk => rmii_ref_clk,
|
||||
rst => rmii_rst,
|
||||
data_in => phy_in.tx_data_en,
|
||||
data_out => temp_tx_data_en
|
||||
);
|
||||
temp_tx_data <= phy_in.tx_data; -- TODO: Again, we are making assumptions about the clock relationship here
|
||||
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
cdc_strobe_inst : entity work.cdc_strobe
|
||||
generic map(
|
||||
SYNCHRONIZERS => 2
|
||||
)
|
||||
port map(
|
||||
a_clk => rmii_ref_clk,
|
||||
a_rst => rmii_rst,
|
||||
a_in => temp_tx_data_ack,
|
||||
b_clk => clk,
|
||||
b_rst => rst,
|
||||
b_out => phy_out.tx_data_ack
|
||||
);
|
||||
|
||||
synchronizer_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 2
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
data_in => temp_tx_active,
|
||||
data_out => phy_out.tx_active
|
||||
);
|
||||
|
||||
end generate cdc_or_register;
|
||||
|
||||
txp : process(rmii_ref_clk, rmii_rst) is
|
||||
begin
|
||||
if rmii_rst then
|
||||
state <= IDLE;
|
||||
|
||||
elsif rising_edge(rmii_ref_clk) then
|
||||
sr <= "00" & sr(sr'high downto 2);
|
||||
if dibit_cnt = 3 then
|
||||
dibit_cnt <= 0;
|
||||
else
|
||||
dibit_cnt <= dibit_cnt + 1;
|
||||
end if;
|
||||
if byte_done then
|
||||
if byte_cnt > 0 then
|
||||
byte_cnt <= byte_cnt - 1;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when IDLE =>
|
||||
sr <= temp_tx_data;
|
||||
dibit_cnt <= 0;
|
||||
if temp_tx_data_en then
|
||||
state <= DATA;
|
||||
end if;
|
||||
|
||||
when DATA =>
|
||||
if byte_done then
|
||||
if temp_tx_data_en then
|
||||
sr <= temp_tx_data;
|
||||
else
|
||||
state <= IPG;
|
||||
byte_cnt <= IPG_SIZE_BYTES - 1;
|
||||
end if;
|
||||
end if;
|
||||
when IPG =>
|
||||
if block_done then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process txp;
|
||||
byte_done <= '1' when dibit_cnt = 3 else '0';
|
||||
block_done <= '1' when (byte_cnt = 0) and (byte_done = '1') else '0';
|
||||
temp_tx_data_ack <= '1' when (state = DATA) and (byte_done = '1') and (temp_tx_data_en = '1') else '0';
|
||||
temp_tx_active <= '1' when state = DATA else '0';
|
||||
|
||||
rmii_txd <= sr(1 downto 0);
|
||||
rmii_tx_en <= '1' when state = DATA else '0';
|
||||
end block transmitter;
|
||||
end architecture rtl;
|
||||
|
@ -1,153 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_udp.vhd : Ethernet OSI Layer 4, Transport (UDP)
|
||||
-- Implements UDP frame encoding and decoding.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use work.trashernet_pkg.all;
|
||||
|
||||
entity trashernet_udp is
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- IPv4 application interface
|
||||
ipv4_protocol_in : out ipv4_protocol_in_t; -- IPv4 IF (out from IP Protocol)
|
||||
ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 IF (into IP Protocol)
|
||||
|
||||
-- UDP application interface
|
||||
udp_out : out udp_out_t; -- UDP application IF (out from UDP)
|
||||
udp_in : in udp_in_t -- UDP application IF (into UDP)
|
||||
);
|
||||
end entity trashernet_udp;
|
||||
|
||||
architecture rtl of trashernet_udp is
|
||||
begin
|
||||
receiver : block
|
||||
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
|
||||
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
|
||||
alias sr_source_port is sr(0 to 1);
|
||||
alias sr_destination_port is sr(2 to 3);
|
||||
alias sr_length is sr(4 to 5);
|
||||
alias sr_source_checksum is sr(6 to 7);
|
||||
signal bytecount : integer range 0 to BYTECOUNT_HEADER - 1;
|
||||
|
||||
type state_t is (HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
begin
|
||||
rxp : process(rst, clk) is
|
||||
begin
|
||||
if rst then
|
||||
state <= HEADER;
|
||||
udp_out.rx_header_rcv <= '0';
|
||||
udp_out.rx_error_stb <= '0';
|
||||
udp_out.rx_ok_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
udp_out.rx_header_rcv <= '0';
|
||||
udp_out.rx_error_stb <= ipv4_protocol_out.rx_error_stb;
|
||||
udp_out.rx_ok_stb <= ipv4_protocol_out.rx_ok_stb;
|
||||
|
||||
case state is
|
||||
when HEADER =>
|
||||
if ipv4_protocol_out.rx_data_valid then
|
||||
sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data;
|
||||
if bytecount = BYTECOUNT_HEADER - 1 then
|
||||
state <= PAYLOAD;
|
||||
udp_out.rx_header_rcv <= '1';
|
||||
else
|
||||
bytecount <= bytecount + 1;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when PAYLOAD =>
|
||||
udp_out.rx_data <= ipv4_protocol_out.rx_data;
|
||||
udp_out.rx_data_valid <= ipv4_protocol_out.rx_data_valid;
|
||||
end case;
|
||||
|
||||
if ipv4_protocol_out.rx_header_rcv or ipv4_protocol_out.rx_error_stb or ipv4_protocol_out.rx_ok_stb then
|
||||
state <= HEADER;
|
||||
bytecount <= 0;
|
||||
end if;
|
||||
end if;
|
||||
end process rxp;
|
||||
|
||||
udp_out.rx_header.source_port <= unsigned(std_logic_vector'(sr_source_port(0) & sr_source_port(1)));
|
||||
udp_out.rx_header.destination_port <= unsigned(std_logic_vector'(sr_destination_port(2) & sr_destination_port(3)));
|
||||
udp_out.rx_header.length <= unsigned(std_logic_vector'(sr_length(4) & sr_length(5))) - BYTECOUNT_HEADER;
|
||||
udp_out.rx_header.ipv4_header <= ipv4_protocol_out.rx_header;
|
||||
end block receiver;
|
||||
|
||||
transmitter : block
|
||||
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
|
||||
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
|
||||
signal byte_cnt : integer range 0 to sr'length - 1;
|
||||
|
||||
type state_t is (IDLE, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
begin
|
||||
txp : process(rst, clk) is
|
||||
variable full_length : udp_length_t;
|
||||
begin
|
||||
if rst then
|
||||
udp_out.tx_data_ack <= '0';
|
||||
udp_out.tx_err_stb <= '0';
|
||||
udp_out.tx_ok_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
udp_out.tx_data_ack <= '0';
|
||||
udp_out.tx_err_stb <= ipv4_protocol_out.tx_err_stb;
|
||||
udp_out.tx_ok_stb <= ipv4_protocol_out.tx_ok_stb;
|
||||
|
||||
if ipv4_protocol_out.tx_data_ack = '1' then
|
||||
if byte_cnt > 0 then
|
||||
byte_cnt <= byte_cnt - 1;
|
||||
end if;
|
||||
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||
end if;
|
||||
case state is
|
||||
when IDLE =>
|
||||
if udp_in.tx_en then
|
||||
full_length := udp_in.tx_length + BYTECOUNT_HEADER;
|
||||
sr <= byte_vector'(byte(udp_in.tx_source_port(15 downto 8)), byte(udp_in.tx_source_port(7 downto 0)), --
|
||||
byte(udp_in.tx_destination_port(15 downto 8)), byte(udp_in.tx_destination_port(7 downto 0)), --
|
||||
byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), --
|
||||
x"00", x"00");
|
||||
byte_cnt <= sr'length - 1;
|
||||
ipv4_protocol_in.tx_length <= full_length;
|
||||
ipv4_protocol_in.tx_ip_address <= udp_in.tx_ip_address;
|
||||
state <= HEADER;
|
||||
end if;
|
||||
|
||||
when HEADER =>
|
||||
if byte_cnt = 0 then
|
||||
state <= PAYLOAD;
|
||||
end if;
|
||||
|
||||
when PAYLOAD =>
|
||||
if ipv4_protocol_out.tx_data_ack then
|
||||
sr(sr'low) <= udp_in.tx_data;
|
||||
udp_out.tx_data_ack <= '1';
|
||||
if not udp_in.tx_en then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process txp;
|
||||
|
||||
ipv4_protocol_in.tx_data <= sr(sr'low);
|
||||
ipv4_protocol_in.tx_en <= '1' when (state /= IDLE) else '0';
|
||||
end block transmitter;
|
||||
|
||||
end architecture rtl;
|
@ -1,139 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_udpprot.vhd : Ethernet OSI Layer 4, Transport (UDP)
|
||||
-- Provides a convenient port-based muxed interface for UDP connections.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use work.trashernet_pkg.all;
|
||||
|
||||
entity trashernet_udpprot is
|
||||
generic(
|
||||
ROUND_ROBIN : boolean := true -- Prioritize, but prevent one channel from hogging all bandwidth (may add one cycle of latency between frames)
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- UDP application interface
|
||||
udp_out : in udp_out_t; -- UDP application IF (out from UDP)
|
||||
udp_in : out udp_in_t; -- UDP application IF (into UDP)
|
||||
|
||||
-- UDP protocols interface
|
||||
udpprot_rx_out : out udpprot_rx_out_vector; -- UDP Application IF for receiving data (out from UDP)
|
||||
udpprot_rx_in : in udpprot_rx_in_vector; -- UDP Application IF for receiving data (into UDP)
|
||||
udpprot_tx_out : out udpprot_tx_out_vector; -- UDP Application IF for transmitting data (out from UDP)
|
||||
udpprot_tx_in : in udpprot_tx_in_vector -- UDP Application IF for transmitting data (in from UDP)
|
||||
);
|
||||
end entity trashernet_udpprot;
|
||||
|
||||
architecture rtl of trashernet_udpprot is
|
||||
constant RX_SEL_PROTOCOL_NONE : integer := udpprot_rx_out'low - 1;
|
||||
|
||||
signal rx_sel : integer range RX_SEL_PROTOCOL_NONE to udpprot_rx_out'high;
|
||||
signal tx_sel : integer range udpprot_tx_out'low to udpprot_tx_out'high;
|
||||
begin
|
||||
assert udpprot_tx_out'length = udpprot_tx_in'length report "UDP in and out ports must have the same length" severity FAILURE;
|
||||
assert udpprot_rx_out'length = udpprot_rx_in'length report "UDP in and out ports must have the same length" severity FAILURE;
|
||||
|
||||
rx : block
|
||||
signal rx_header_rcv_delayed : std_logic;
|
||||
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
rx_sel <= RX_SEL_PROTOCOL_NONE;
|
||||
rx_header_rcv_delayed <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
rx_header_rcv_delayed <= udp_out.rx_header_rcv;
|
||||
|
||||
if udp_out.rx_header_rcv then
|
||||
rx_sel <= RX_SEL_PROTOCOL_NONE;
|
||||
for i in udpprot_rx_out'range loop
|
||||
if (udp_out.rx_header.destination_port = udpprot_rx_in(i).port_bind) then
|
||||
rx_sel <= i;
|
||||
exit;
|
||||
end if;
|
||||
end loop;
|
||||
end if;
|
||||
end if;
|
||||
end process arb;
|
||||
|
||||
mux : for i in udpprot_rx_out'range generate
|
||||
udpprot_rx_out(i).rx_data <= udp_out.rx_data;
|
||||
udpprot_rx_out(i).rx_header <= udp_out.rx_header;
|
||||
|
||||
udpprot_rx_out(i).rx_data_valid <= udp_out.rx_data_valid when rx_sel = i else '0';
|
||||
udpprot_rx_out(i).rx_error_stb <= udp_out.rx_error_stb when rx_sel = i else '0';
|
||||
udpprot_rx_out(i).rx_ok_stb <= udp_out.rx_ok_stb when rx_sel = i else '0';
|
||||
udpprot_rx_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0';
|
||||
end generate mux;
|
||||
|
||||
tx_mux : for i in udpprot_tx_out'range generate
|
||||
udpprot_tx_out(i).tx_data_ack <= udp_out.tx_data_ack when tx_sel = i else '0';
|
||||
udpprot_tx_out(i).tx_err_stb <= udp_out.tx_err_stb when tx_sel = i else '0';
|
||||
udpprot_tx_out(i).tx_ok_stb <= udp_out.tx_ok_stb when tx_sel = i else '0';
|
||||
end generate;
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, TXD, WAITDONE);
|
||||
signal state : state_t;
|
||||
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
tx_sel <= udpprot_tx_in'left;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
case state is
|
||||
when IDLE =>
|
||||
tx_sel <= udpprot_tx_out'left;
|
||||
for i in udpprot_tx_in'range loop
|
||||
if (udpprot_tx_in(i).tx_en = '1') and (i >= tx_sel) then
|
||||
tx_sel <= i;
|
||||
state <= TXD;
|
||||
exit; -- Prioritize according to vector
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
when TXD =>
|
||||
state <= WAITDONE when (not udp_in.tx_en);
|
||||
|
||||
when WAITDONE =>
|
||||
if udp_out.tx_err_stb or udp_out.tx_ok_stb then
|
||||
state <= IDLE;
|
||||
if (not ROUND_ROBIN) or (tx_sel = udpprot_tx_out'high) then
|
||||
tx_sel <= udpprot_tx_out'left;
|
||||
else
|
||||
tx_sel <= tx_sel + 1;
|
||||
end if;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process arb;
|
||||
|
||||
demux : block
|
||||
begin
|
||||
udp_in.tx_data <= udpprot_tx_in(tx_sel).tx_data;
|
||||
udp_in.tx_en <= udpprot_tx_in(tx_sel).tx_en;
|
||||
udp_in.tx_ip_address <= udpprot_tx_in(tx_sel).tx_ip_address;
|
||||
udp_in.tx_length <= udpprot_tx_in(tx_sel).tx_length;
|
||||
udp_in.tx_source_port <= udpprot_tx_in(tx_sel).tx_source_port;
|
||||
udp_in.tx_destination_port <= udpprot_tx_in(tx_sel).tx_destination_port;
|
||||
end block demux;
|
||||
end block tx;
|
||||
end architecture rtl;
|
Loading…
x
Reference in New Issue
Block a user