Compare commits
	
		
			86 Commits
		
	
	
		
			ef6b6378f8
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3a42c865c6 | |||
| 474d444f59 | |||
| a98a9cbf06 | |||
| 6e616c5f35 | |||
| f13e9bf907 | |||
| ac553483df | |||
| faa915824a | |||
| 3c592dbd23 | |||
| ba0cbed9bf | |||
| e7db2df389 | |||
| 3e1a53abaf | |||
| c9f6560091 | |||
| 7a0da0f91d | |||
| f5b691fae5 | |||
| 9258edd0d9 | |||
| f62a464e7e | |||
| 9f0f8c2775 | |||
| 6dc42267fa | |||
| 1ed42918fc | |||
| f67e8d8b37 | |||
| 154604aacd | |||
| 02a00e875d | |||
| 7379df7b74 | |||
| 21a0d0e69a | |||
| 943febcb99 | |||
| da7e329939 | |||
| 4bce111d69 | |||
| 2190f146a0 | |||
| 91cdb13bb3 | |||
| 0db98483dc | |||
| 6de8d24537 | |||
| 87bc9ce3d7 | |||
| fdcb1cb719 | |||
| a0c9df28f1 | |||
| 0d7981f801 | |||
| 172a11070b | |||
| 9d9b3b1cce | |||
| e421ba0dff | |||
| 277d01737d | |||
| a19de6d0c8 | |||
| 1c08d48861 | |||
| ccb7d6c0da | |||
| 8d9ca9e784 | |||
| 3240f363ba | |||
| a5d5ca280d | |||
| 651b6bb11e | |||
| 4804674d8e | |||
| 1a41120afa | |||
| 5f7704cf63 | |||
| 5f389b0c7b | |||
| 7bb82a9acf | |||
| 3c1c14f953 | |||
| 50b65bccec | |||
| 461c7ebce1 | |||
| eab8783c6b | |||
| 8f4e996d57 | |||
| e420b08eb2 | |||
| 7aa1b56bf9 | |||
| 8f3420bd54 | |||
| 4b4e5fdbfa | |||
| 0e80900426 | |||
| fd721ae24f | |||
| 82d4e6808b | |||
| 1c281bc0f7 | |||
| c4f4cfe68e | |||
| f224aee487 | |||
| f45ded2ca9 | |||
| 7d86f6a262 | |||
| 313711dbc0 | |||
| 0f7437e901 | |||
| 5c27b685dc | |||
| 6beb9c8346 | |||
| 5b54a596c1 | |||
| 61812c133f | |||
| 075f935e12 | |||
| a9039a175c | |||
| 4a39c3d638 | |||
| d1150fb7db | |||
| d1e0143c9d | |||
| 1bcddf4273 | |||
| afb545cd02 | |||
| 4e39e3b371 | |||
| a2edc540b2 | |||
| 6f8bb7ba54 | |||
| 76eaff20fd | |||
| 37714d5c2d | 
| @ -0,0 +1,7 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||||
|  | <launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType"> | ||||||
|  |     <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/> | ||||||
|  |     <stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="com.sigasi.hdt.toolchains.vunit.builder"/> | ||||||
|  |     <mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/> | ||||||
|  |     <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/> | ||||||
|  | </launchConfiguration> | ||||||
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | # VUnit | ||||||
|  | vunit_out | ||||||
|  | 
 | ||||||
|  | # Cocotb | ||||||
|  | __pycache__ | ||||||
|  | *.o | ||||||
|  | cocotb/results.xml | ||||||
|  | cocotb/sim_build | ||||||
|  | cocotb/wave.ghw | ||||||
|  | 
 | ||||||
|  | cocotb/cocotb_top_mac_test | ||||||
|  | cocotb/cocotb_top_hwitl | ||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | [submodule "cocotb/cocotb_helpers"] | ||||||
|  | 	path = cocotb/cocotb_helpers | ||||||
|  | 	url = https://git.notsyncing.net/fpga/cocotb_helpers.git | ||||||
							
								
								
									
										10
									
								
								.library_mapping.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.library_mapping.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | <?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="design" Library="design"/> | ||||||
|  |   <Mappings Location="Common Libraries/IEEE" Library="ieee"/> | ||||||
|  |   <Mappings Location="Common Libraries/IEEE Synopsys" Library="ieee"/> | ||||||
|  |   <Mappings Location="Common Libraries" Library="not mapped"/> | ||||||
|  |   <Mappings Location="Common Libraries/STD" Library="std"/> | ||||||
|  |   <Mappings Location="trashernet" Library="trashernet"/> | ||||||
|  | </com.sigasi.hdt.shared.librarymapping.model:LibraryMappings> | ||||||
							
								
								
									
										41
									
								
								.project
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.project
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <projectDescription> | ||||||
|  | 	<name>trashernet</name> | ||||||
|  | 	<comment></comment> | ||||||
|  | 	<projects> | ||||||
|  | 	</projects> | ||||||
|  | 	<buildSpec> | ||||||
|  | 		<buildCommand> | ||||||
|  | 			<name>org.eclipse.xtext.ui.shared.xtextBuilder</name> | ||||||
|  | 			<arguments> | ||||||
|  | 			</arguments> | ||||||
|  | 		</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> | ||||||
|  | 	</natures> | ||||||
|  | 	<linkedResources> | ||||||
|  | 		<link> | ||||||
|  | 			<name>Common Libraries</name> | ||||||
|  | 			<type>2</type> | ||||||
|  | 			<locationURI>virtual:/virtual</locationURI> | ||||||
|  | 		</link> | ||||||
|  | 		<link> | ||||||
|  | 			<name>Common Libraries/IEEE</name> | ||||||
|  | 			<type>2</type> | ||||||
|  | 			<locationURI>sigasiresource:/vhdl/2008/IEEE</locationURI> | ||||||
|  | 		</link> | ||||||
|  | 		<link> | ||||||
|  | 			<name>Common Libraries/IEEE Synopsys</name> | ||||||
|  | 			<type>2</type> | ||||||
|  | 			<locationURI>sigasiresource:/vhdl/2008/IEEE%20Synopsys</locationURI> | ||||||
|  | 		</link> | ||||||
|  | 		<link> | ||||||
|  | 			<name>Common Libraries/STD</name> | ||||||
|  | 			<type>2</type> | ||||||
|  | 			<locationURI>sigasiresource:/vhdl/2008/STD</locationURI> | ||||||
|  | 		</link> | ||||||
|  | 	</linkedResources> | ||||||
|  | </projectDescription> | ||||||
							
								
								
									
										2
									
								
								.settings/com.sigasi.hdt.verilog.linting.prefs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.settings/com.sigasi.hdt.verilog.linting.prefs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | eclipse.preferences.version=1 | ||||||
|  | isEnabled=true | ||||||
							
								
								
									
										1
									
								
								.settings/com.sigasi.hdt.verilog.version.prefs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.settings/com.sigasi.hdt.verilog.version.prefs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <project>=sv2012 | ||||||
							
								
								
									
										2
									
								
								.settings/com.sigasi.hdt.vhdl.linting.prefs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.settings/com.sigasi.hdt.vhdl.linting.prefs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | eclipse.preferences.version=1 | ||||||
|  | isEnabled=true | ||||||
							
								
								
									
										1
									
								
								.settings/com.sigasi.hdt.vhdl.version.prefs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.settings/com.sigasi.hdt.vhdl.version.prefs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <project>=2008 | ||||||
							
								
								
									
										5
									
								
								.settings/org.eclipse.core.resources.prefs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.settings/org.eclipse.core.resources.prefs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +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 | ||||||
							
								
								
									
										36
									
								
								README.MD
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								README.MD
									
									
									
									
									
								
							| @ -4,8 +4,10 @@ | |||||||
| 
 | 
 | ||||||
| Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to cover all OSI layers: | Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to cover all OSI layers: | ||||||
| 
 | 
 | ||||||
| * Layer 1, Physical: `trashernet_phy` | * Layer 1, Physical: `trashernet_phy`, (`trashernet_rmii`) | ||||||
| * Layer 2, Data link: `trashernet_mac` | * 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: | When writing it, the following were the main design philosophies: | ||||||
| 
 | 
 | ||||||
| @ -15,13 +17,37 @@ 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. | **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 | ## Hardware | ||||||
| 
 | 
 | ||||||
| TBD. TL;DR: |  | ||||||
| 
 | 
 | ||||||
| * RX: Use resistors behind Ethernet magnetics to reduce Ethernet RX voltage levels (0 +-2.5V) to LVPECL voltage levels (1.65V +-400mV). Or LVDS if your FPGA can't handle LVPECL. | * RX: Use resistors behind Ethernet magnetics to reduce Ethernet RX voltage levels (0 +-2.5V) to LVPECL voltage levels (1.65V +-400mV). Or LVDS if your FPGA can't handle LVPECL. Also, pull up/down resistors help to avoid glitchy behavior with no cable connected or little data activity. | ||||||
| * TX: Meh, a series current limiting resistor is good enough. | * TX: Meh, a series current limiting resistor is good enough. Drive two outputs differentially to increase the voltage swing. (Technically not true antivalent signals as there's also the "off" state with both disabled.) | ||||||
|  | 
 | ||||||
|  | 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 | ## License | ||||||
| 
 | 
 | ||||||
| This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. | This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. | ||||||
|  | 
 | ||||||
|  | The Trashernet logo uses graphics from [svgrepo.com](https://svgrepo.com), which are released under the Creative Commons CC0 license, granting the right to freely adapt and use them in any context. Thank you for making these available: | ||||||
|  | 
 | ||||||
|  | * Trash can: https://www.svgrepo.com/svg/66604/trash | ||||||
|  | * Ethernet jack: https://www.svgrepo.com/svg/292518/ethernet | ||||||
|  | 
 | ||||||
|  | The font used in the logo, "White Rabbit" by Matthew Welch, is also released on a free-to-use basis. Thank you! | ||||||
|  | 
 | ||||||
|  | * White Rabbit: https://www.fontsquirrel.com/fonts/white-rabbit | ||||||
|  | |||||||
| @ -19,12 +19,12 @@ end entity bench_crc; | |||||||
| 
 | 
 | ||||||
| architecture bench of bench_crc is | architecture bench of bench_crc is | ||||||
| 	constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7"; | 	constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7"; | ||||||
| 	signal clk          : std_logic; | 	signal   clk        : std_logic; | ||||||
| 	signal rst          : std_logic; | 	signal   rst        : std_logic; | ||||||
| 	signal data         : std_logic_vector(7 downto 0); | 	signal   data       : std_logic_vector(7 downto 0); | ||||||
| 	signal data_valid   : std_logic; | 	signal   data_valid : std_logic; | ||||||
| 	signal crc_clear    : std_logic; | 	signal   crc_clear  : std_logic; | ||||||
| 	signal crc_out      : std_logic_vector(POLYNOMIAL'range); | 	signal   crc_out    : std_logic_vector(POLYNOMIAL'range); | ||||||
| begin | begin | ||||||
| 	crc_inst : entity trashernet.crc | 	crc_inst : entity trashernet.crc | ||||||
| 		generic map( | 		generic map( | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ package body bench_pkg is | |||||||
| 				wait for 50 ns; | 				wait for 50 ns; | ||||||
| 			end loop; | 			end loop; | ||||||
| 		end loop; | 		end loop; | ||||||
| 		wait for 16 us; -- IPG | 		wait for 16 us;                 -- IPG | ||||||
| 		report "Frame TX complete"; | 		report "Frame TX complete"; | ||||||
| 	end procedure send_data; | 	end procedure send_data; | ||||||
| end package body bench_pkg; | end package body bench_pkg; | ||||||
|  | |||||||
| @ -18,7 +18,13 @@ use work.bench_pkg.all; | |||||||
| library trashernet; | library trashernet; | ||||||
| use trashernet.trashernet_pkg.all; | use trashernet.trashernet_pkg.all; | ||||||
| 
 | 
 | ||||||
|  | library vunit_lib; | ||||||
|  | context vunit_lib.vunit_context; | ||||||
|  | 
 | ||||||
| entity bench_trashernet_mac is | entity bench_trashernet_mac is | ||||||
|  | 	generic( | ||||||
|  | 		runner_cfg : string | ||||||
|  | 	); | ||||||
| end entity bench_trashernet_mac; | end entity bench_trashernet_mac; | ||||||
| 
 | 
 | ||||||
| architecture bench of bench_trashernet_mac is | architecture bench of bench_trashernet_mac is | ||||||
| @ -37,6 +43,8 @@ architecture bench of bench_trashernet_mac is | |||||||
| 
 | 
 | ||||||
| 	constant TEST_BENCH_LOOPBACK : boolean := true; | 	constant TEST_BENCH_LOOPBACK : boolean := true; | ||||||
| 
 | 
 | ||||||
|  | 	signal mac_tx_start : std_logic := '0'; | ||||||
|  | 
 | ||||||
| begin | begin | ||||||
| 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
| 		generic map( | 		generic map( | ||||||
| @ -144,6 +152,8 @@ begin | |||||||
| 
 | 
 | ||||||
| 	mac_tx : process is | 	mac_tx : process is | ||||||
| 	begin | 	begin | ||||||
|  | 		wait on mac_tx_start; | ||||||
|  | 
 | ||||||
| 		mac_in.tx_mac_data               <= x"11"; | 		mac_in.tx_mac_data               <= x"11"; | ||||||
| 		mac_in.tx_header.mac_destination <= (x"12", x"23", x"34", x"45", x"56", x"67"); | 		mac_in.tx_header.mac_destination <= (x"12", x"23", x"34", x"45", x"56", x"67"); | ||||||
| 		mac_in.tx_header.mac_source      <= (x"a2", x"a3", x"a4", x"a5", x"a6", x"a7"); | 		mac_in.tx_header.mac_source      <= (x"a2", x"a3", x"a4", x"a5", x"a6", x"a7"); | ||||||
| @ -159,8 +169,27 @@ begin | |||||||
| 		wait until rising_edge(mac_out.tx_mac_data_ack); | 		wait until rising_edge(mac_out.tx_mac_data_ack); | ||||||
| 		wait until rising_edge(clk); | 		wait until rising_edge(clk); | ||||||
| 		mac_in.tx_mac_data_en <= '0'; | 		mac_in.tx_mac_data_en <= '0'; | ||||||
| 
 |  | ||||||
| 		wait; |  | ||||||
| 	end process mac_tx; | 	end process mac_tx; | ||||||
| 
 | 
 | ||||||
|  | 	test : process is | ||||||
|  | 	begin | ||||||
|  | 		wait for 1 ns; | ||||||
|  | 
 | ||||||
|  | 		test_runner_setup(runner, runner_cfg); | ||||||
|  | 
 | ||||||
|  | 		while test_suite loop | ||||||
|  | 
 | ||||||
|  | 			if run("mac_tx_rx_simple") then | ||||||
|  | 				mac_tx_start <= not mac_tx_start; | ||||||
|  | 				wait on mac_out.rx_mac_crc_ok, mac_out.rx_mac_crc_error for 1 ms; | ||||||
|  | 				assert mac_out.rx_mac_crc_ok = '1' report "Did not receive CRC OK" severity error; | ||||||
|  | 			end if; | ||||||
|  | 		end loop; | ||||||
|  | 
 | ||||||
|  | 		wait for 1 ns; | ||||||
|  | 		wait until rising_edge(clk); | ||||||
|  | 
 | ||||||
|  | 		test_runner_cleanup(runner); | ||||||
|  | 	end process test; | ||||||
|  | 
 | ||||||
| end architecture bench; | end architecture bench; | ||||||
|  | |||||||
| @ -22,15 +22,14 @@ entity bench_trashernet_phy is | |||||||
| end entity bench_trashernet_phy; | end entity bench_trashernet_phy; | ||||||
| 
 | 
 | ||||||
| architecture bench of bench_trashernet_phy is | architecture bench of bench_trashernet_phy is | ||||||
| 	signal clk            : std_logic; | 	signal clk     : std_logic; | ||||||
| 	signal phy_clk        : std_logic; | 	signal phy_clk : std_logic; | ||||||
| 	signal rst            : std_logic; | 	signal rst     : std_logic; | ||||||
| 	signal phy_out : phy_out_t; | 	signal phy_out : phy_out_t; | ||||||
| 	signal phy_in : phy_in_t; | 	signal phy_in  : phy_in_t; | ||||||
| 	signal rx_p : std_logic; | 	signal rx_p    : std_logic; | ||||||
| 	signal tx_p : std_logic; | 	signal tx_p    : std_logic; | ||||||
| 	signal tx_n : std_logic; | 	signal tx_n    : std_logic; | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| begin | begin | ||||||
| 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
| @ -39,14 +38,14 @@ begin | |||||||
| 			F_CLK_PHY => 100000000 | 			F_CLK_PHY => 100000000 | ||||||
| 		) | 		) | ||||||
| 		port map( | 		port map( | ||||||
| 			clk => clk, | 			clk     => clk, | ||||||
| 			phy_clk => phy_clk, | 			phy_clk => phy_clk, | ||||||
| 			rst => rst, | 			rst     => rst, | ||||||
| 			phy_out => phy_out, | 			phy_out => phy_out, | ||||||
| 			phy_in => phy_in, | 			phy_in  => phy_in, | ||||||
| 			rx_p => rx_p, | 			rx_p    => rx_p, | ||||||
| 			tx_p => tx_p, | 			tx_p    => tx_p, | ||||||
| 			tx_n => tx_n | 			tx_n    => tx_n | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 	clock_driver : process | 	clock_driver : process | ||||||
|  | |||||||
							
								
								
									
										218
									
								
								bench/bench_trashernet_rmii.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								bench/bench_trashernet_rmii.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               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; | ||||||
							
								
								
									
										42
									
								
								bench/pll0.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								bench/pll0.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- pll0.vhd : Simulates the PLL component present in some demo designs | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | entity pll0 is | ||||||
|  | 	port( | ||||||
|  | 		CLK   : in  std_logic; | ||||||
|  | 		CLKOP : out std_logic; | ||||||
|  | 		LOCK  : out std_logic | ||||||
|  | 	); | ||||||
|  | end entity pll0; | ||||||
|  | 
 | ||||||
|  | architecture bench of pll0 is | ||||||
|  | begin | ||||||
|  | 	clock_driver : process | ||||||
|  | 		constant period : time := 7.1429 ns; -- 140 MHz configured clock | ||||||
|  | 	begin | ||||||
|  | 		CLKOP <= '0'; | ||||||
|  | 		LOCK  <= '0'; | ||||||
|  | 
 | ||||||
|  | 		wait until CLK'event; | ||||||
|  | 		wait for 12 ns; | ||||||
|  | 		LOCK <= '1'; | ||||||
|  | 
 | ||||||
|  | 		loop | ||||||
|  | 			CLKOP <= '0'; | ||||||
|  | 			wait for period / 2; | ||||||
|  | 			CLKOP <= '1'; | ||||||
|  | 			wait for period / 2; | ||||||
|  | 		end loop; | ||||||
|  | 	end process clock_driver; | ||||||
|  | end architecture bench; | ||||||
							
								
								
									
										19
									
								
								cocotb/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								cocotb/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | # defaults
 | ||||||
|  | 
 | ||||||
|  | SIM ?= ghdl | ||||||
|  | TOPLEVEL_LANG ?= vhdl | ||||||
|  | BASE = $(PWD)/.. | ||||||
|  | BENCHTOP ?= cocotb_top_mac_test | ||||||
|  | 
 | ||||||
|  | COMPILE_ARGS=--std=08 | ||||||
|  | # SIM_ARGS ?= --wave=wave.ghw
 | ||||||
|  | 
 | ||||||
|  | VHDL_SOURCES_trashernet += $(BASE)/trashernet/*.vhd | ||||||
|  | VHDL_SOURCES_design += $(BASE)/design/*.vhd | ||||||
|  | VHDL_SOURCES_design += $(BASE)/bench/pll0.vhd | ||||||
|  | VHDL_SOURCES += $(BASE)/cocotb/*.vhd | ||||||
|  | 
 | ||||||
|  | TOPLEVEL ?= $(BENCHTOP) | ||||||
|  | MODULE ?= $(BENCHTOP) | ||||||
|  | 
 | ||||||
|  | include $(shell cocotb-config --makefiles)/Makefile.sim | ||||||
							
								
								
									
										7
									
								
								cocotb/cfg_cocotb_top_hwitl_eth.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cocotb/cfg_cocotb_top_hwitl_eth.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | 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
									
								
								cocotb/cocotb_helpers
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								cocotb/cocotb_helpers
									
									
									
									
									
										Submodule
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Subproject commit e2c72ef61d7742fbcb7e048bc928446ed4b56a82 | ||||||
							
								
								
									
										90
									
								
								cocotb/cocotb_top_hwitl.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								cocotb/cocotb_top_hwitl.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- cocotb_top_hwitl.vhd : Test bench for cocotb HW-in-the-loop tests | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | library design; | ||||||
|  | use design.all; | ||||||
|  | 
 | ||||||
|  | entity cocotb_top_hwitl is | ||||||
|  | end entity cocotb_top_hwitl; | ||||||
|  | 
 | ||||||
|  | architecture bench of cocotb_top_hwitl is | ||||||
|  | 	-- DUT signals | ||||||
|  | 	signal clk        : std_logic; | ||||||
|  | 	signal rst_n      : std_logic; | ||||||
|  | 	signal rx_p       : std_logic; | ||||||
|  | 	signal tx_p       : std_logic; | ||||||
|  | 	signal tx_n       : std_logic; | ||||||
|  | 	signal led_n      : std_logic_vector(7 downto 0); | ||||||
|  | 	signal button_n   : std_logic_vector(3 downto 0); | ||||||
|  | 	signal debug_data : std_logic_vector(7 downto 0); | ||||||
|  | 
 | ||||||
|  | 	-- Generic test bench | ||||||
|  | 	signal bench_ready : std_logic := '0'; | ||||||
|  | begin | ||||||
|  | 	top_mac_test_inst : entity top_hwitl(eth) -- TODO: I haven't managed to get configurations working with cocotb yet, so changing this line changes the test case :( | ||||||
|  | 		port map( | ||||||
|  | 			clk        => clk, | ||||||
|  | 			rst_n      => rst_n, | ||||||
|  | 			rx_p       => rx_p, | ||||||
|  | 			tx_p       => tx_p, | ||||||
|  | 			tx_n       => tx_n, | ||||||
|  | 			led_n      => led_n, | ||||||
|  | 			button_n   => button_n, | ||||||
|  | 			debug_data => debug_data | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	clock_driver : process | ||||||
|  | 		constant period : time := 20 ns; | ||||||
|  | 	begin | ||||||
|  | 		clk <= '0'; | ||||||
|  | 		wait for period / 2; | ||||||
|  | 		clk <= '1'; | ||||||
|  | 		wait for period / 2; | ||||||
|  | 	end process clock_driver; | ||||||
|  | 
 | ||||||
|  | 	rstsim : process is | ||||||
|  | 	begin | ||||||
|  | 		rst_n       <= '0'; | ||||||
|  | 		wait for 400 ns; | ||||||
|  | 		rst_n       <= '1'; | ||||||
|  | 		wait for 100 ns; | ||||||
|  | 		wait until rising_edge(clk); | ||||||
|  | 		bench_ready <= '1'; | ||||||
|  | 		wait; | ||||||
|  | 	end process rstsim; | ||||||
|  | 
 | ||||||
|  | 	cocovc_eth_inst : entity work.cocovc_eth | ||||||
|  | 		port map( | ||||||
|  | 			rx_p => tx_p, | ||||||
|  | 			rx_n => tx_n, | ||||||
|  | 			tx_p => rx_p, | ||||||
|  | 			tx_n => open | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	test_seq : process is | ||||||
|  | 	begin | ||||||
|  | 		wait until bench_ready = '1'; | ||||||
|  | 
 | ||||||
|  | 		button_n <= (others => '1'); | ||||||
|  | 		wait for 1 us; | ||||||
|  | 
 | ||||||
|  | 		wait until rising_edge(clk); | ||||||
|  | 		button_n(0) <= '0'; | ||||||
|  | 		wait until rising_edge(clk); | ||||||
|  | 		button_n(0) <= '1'; | ||||||
|  | 
 | ||||||
|  | 		wait; | ||||||
|  | 	end process test_seq; | ||||||
|  | 
 | ||||||
|  | end architecture bench; | ||||||
							
								
								
									
										48
									
								
								cocotb/cocotb_top_mac_test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cocotb/cocotb_top_mac_test.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | 
 | ||||||
|  | import cocotb | ||||||
|  | from cocotb.triggers import * | ||||||
|  | from cocotb.result import * | ||||||
|  | 
 | ||||||
|  | from cocotb.queue import Queue | ||||||
|  | 
 | ||||||
|  | from cocotb_helpers import buffers | ||||||
|  | 
 | ||||||
|  | ETH_HEAD = b'\x55\x55\x55\x55\xD5' | ||||||
|  | 
 | ||||||
|  | async def timeout(dut): | ||||||
|  | 	await Timer(1, units="ms") | ||||||
|  | 	assert False, "Timeout" | ||||||
|  | 
 | ||||||
|  | async def rxprinter(dut, rx): | ||||||
|  | 	while True: | ||||||
|  | 		frame = await rx.queue.get() | ||||||
|  | 		dut._log.info("RX Frame: " + str(frame)) | ||||||
|  | 
 | ||||||
|  | @cocotb.test() | ||||||
|  | async def my_test(dut): | ||||||
|  | 	"""TBD description.""" | ||||||
|  | 
 | ||||||
|  | 	# Start verification components | ||||||
|  | 	eth_tx = buffers.tx_buffer(dut, "cocovc_eth_inst.cocotb_tx_") | ||||||
|  | 	await eth_tx.start() | ||||||
|  | 	eth_rx = buffers.rx_buffer(dut, "cocovc_eth_inst.cocotb_rx_") | ||||||
|  | 	await eth_rx.start() | ||||||
|  | 
 | ||||||
|  | 	# Start local monitors | ||||||
|  | 	await cocotb.start(rxprinter(dut, eth_rx)) | ||||||
|  | 
 | ||||||
|  | 	# Start timeout | ||||||
|  | 	await cocotb.start(timeout(dut)) | ||||||
|  | 
 | ||||||
|  | 	# Wait for VHDL part to be ready | ||||||
|  | 	await Edge(dut.bench_ready) | ||||||
|  | 
 | ||||||
|  | 	# Start test procedure | ||||||
|  | 	await Timer(400, units="us") # The device will send a frame at startup | ||||||
|  | 
 | ||||||
|  | 	dut._log.info("Send first frame"); # This will trigger a response frame | ||||||
|  | 	await eth_tx.send(ETH_HEAD + b'\x00\xff\xff\x11"3\x00\xff\xff\xaa\xbb\xcc\x00\n\n\t\x08\x07\x06\x05\x04\x03\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x10\x90\xe0'); | ||||||
|  | 	dut._log.info("Send second frame"); | ||||||
|  | 	await eth_tx.send(ETH_HEAD + b'World'); | ||||||
|  | 	await Timer(400, units="us") # Wait for response | ||||||
							
								
								
									
										77
									
								
								cocotb/cocotb_top_mac_test.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								cocotb/cocotb_top_mac_test.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- cocotb_top_mac_test.vhd : Test bench for the MAC test demo. | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | library design; | ||||||
|  | use design.all; | ||||||
|  | 
 | ||||||
|  | entity cocotb_top_mac_test is | ||||||
|  | end entity cocotb_top_mac_test; | ||||||
|  | 
 | ||||||
|  | architecture bench of cocotb_top_mac_test is | ||||||
|  | 	-- DUT signals | ||||||
|  | 	signal clk        : std_logic; | ||||||
|  | 	signal rst_n      : std_logic; | ||||||
|  | 	signal rx_p       : std_logic; | ||||||
|  | 	signal tx_p       : std_logic; | ||||||
|  | 	signal tx_n       : std_logic; | ||||||
|  | 	signal led_n      : std_logic_vector(7 downto 0); | ||||||
|  | 	signal button_n   : std_logic_vector(3 downto 0); | ||||||
|  | 	signal debug_data : std_logic_vector(7 downto 0); | ||||||
|  | 
 | ||||||
|  | 	-- Generic test bench | ||||||
|  | 	signal bench_ready : std_logic := '0'; | ||||||
|  | begin | ||||||
|  | 	top_mac_test_inst : entity design.top_mac_test | ||||||
|  | 		port map( | ||||||
|  | 			clk        => clk, | ||||||
|  | 			rst_n      => rst_n, | ||||||
|  | 			rx_p       => rx_p, | ||||||
|  | 			tx_p       => tx_p, | ||||||
|  | 			tx_n       => tx_n, | ||||||
|  | 			led_n      => led_n, | ||||||
|  | 			button_n   => button_n, | ||||||
|  | 			debug_data => debug_data | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	clock_driver : process | ||||||
|  | 		constant period : time := 20 ns; | ||||||
|  | 	begin | ||||||
|  | 		clk <= '0'; | ||||||
|  | 		wait for period / 2; | ||||||
|  | 		clk <= '1'; | ||||||
|  | 		wait for period / 2; | ||||||
|  | 	end process clock_driver; | ||||||
|  | 
 | ||||||
|  | 	rstsim : process is | ||||||
|  | 	begin | ||||||
|  | 		rst_n       <= '0'; | ||||||
|  | 		wait for 400 ns; | ||||||
|  | 		rst_n       <= '1'; | ||||||
|  | 		wait for 100 ns; | ||||||
|  | 		wait until rising_edge(clk); | ||||||
|  | 		bench_ready <= '1'; | ||||||
|  | 		wait; | ||||||
|  | 	end process rstsim; | ||||||
|  | 
 | ||||||
|  | 	cocovc_eth_inst : entity work.cocovc_eth | ||||||
|  | 		port map( | ||||||
|  | 			rx_p => tx_p, | ||||||
|  | 			rx_n => tx_n, | ||||||
|  | 			tx_p => rx_p, | ||||||
|  | 			tx_n => open | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	button_n <= (others => '1'); | ||||||
|  | 
 | ||||||
|  | end architecture bench; | ||||||
							
								
								
									
										123
									
								
								cocotb/cocovc_eth.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								cocotb/cocovc_eth.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- cocovc_eth.vhd : Cocotb-based verification component for 10M Ethernet. | ||||||
|  | --   Note: This is cheapskated to work with Trashernet. It may not work with | ||||||
|  | --   every 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; | ||||||
|  | 
 | ||||||
|  | entity cocovc_eth is | ||||||
|  | 	port( | ||||||
|  | 		rx_p : in  std_logic; | ||||||
|  | 		rx_n : in  std_logic; | ||||||
|  | 		tx_p : out std_logic; | ||||||
|  | 		tx_n : out std_logic | ||||||
|  | 	); | ||||||
|  | end entity cocovc_eth; | ||||||
|  | 
 | ||||||
|  | architecture bench of cocovc_eth is | ||||||
|  | 	-- Ethernet TX (TB/Python to DUT/VHDL) | ||||||
|  | 	signal cocotb_tx_d  : std_logic_vector(7 downto 0) := x"00"; -- Data to be transferred to VHDL | ||||||
|  | 	signal cocotb_tx_dv : std_logic                    := '0'; -- '1' when `d` holds valid data that is to be processed by VHDL | ||||||
|  | 	signal cocotb_tx_da : std_logic                    := '0'; -- Toggles when VHDL has latched `d`, Python must advance data or deassert `dv` | ||||||
|  | 
 | ||||||
|  | 	-- Ethernet RX (DUT/VHDL to TB/Python) | ||||||
|  | 	signal cocotb_rx_d  : std_logic_vector(7 downto 0); -- Data to be transferred to Python | ||||||
|  | 	signal cocotb_rx_dt : std_logic := '0'; -- Toggles to announce new data (Python has to latch immediately) | ||||||
|  | 	signal cocotb_rx_a  : std_logic := '0'; -- Block active indicator (rising on block start, falling on block end) | ||||||
|  | begin | ||||||
|  | 	-- Ethernet TX | ||||||
|  | 	eth_tx_proc : process is | ||||||
|  | 		variable d : std_logic_vector(cocotb_rx_d'range); | ||||||
|  | 	begin | ||||||
|  | 		tx_p <= '0'; | ||||||
|  | 		wait until cocotb_tx_dv = '1'; | ||||||
|  | 
 | ||||||
|  | 		loop | ||||||
|  | 			exit when not cocotb_tx_dv; | ||||||
|  | 
 | ||||||
|  | 			d            := cocotb_tx_d; | ||||||
|  | 			cocotb_tx_da <= not cocotb_tx_da; | ||||||
|  | 
 | ||||||
|  | 			for j in d'low to d'high loop | ||||||
|  | 				tx_p <= not d(j); | ||||||
|  | 				wait for 50 ns; | ||||||
|  | 				tx_p <= d(j); | ||||||
|  | 				wait for 50 ns; | ||||||
|  | 			end loop; | ||||||
|  | 		end loop; | ||||||
|  | 
 | ||||||
|  | 		wait for 16 us;                 -- Wait IPG | ||||||
|  | 		cocotb_tx_da <= not cocotb_tx_da; -- Signal that end-of-block was latched | ||||||
|  | 
 | ||||||
|  | 	end process eth_tx_proc; | ||||||
|  | 	tx_n <= not tx_p; | ||||||
|  | 
 | ||||||
|  | 	-- Ethernet RX | ||||||
|  | 	eth_rx_proc : process is | ||||||
|  | 		constant SYM_TIME : time                                := 50 ns; | ||||||
|  | 		variable d        : std_logic_vector(cocotb_rx_d'range) := x"00"; | ||||||
|  | 		variable cnt      : integer range d'low to d'high; | ||||||
|  | 		variable bit      : std_logic; | ||||||
|  | 		variable ignore   : boolean; | ||||||
|  | 		variable first    : boolean;    -- Just a cheap workaround because the weather is too nice to implement it properly this afternoon | ||||||
|  | 		procedure latch(value : std_logic) is | ||||||
|  | 		begin | ||||||
|  | 			d(cnt) := value; | ||||||
|  | 			if (cnt = 7) then | ||||||
|  | 				cocotb_rx_d  <= d; | ||||||
|  | 				cocotb_rx_dt <= not cocotb_rx_dt; | ||||||
|  | 				cnt          := 0; | ||||||
|  | 			else | ||||||
|  | 				cnt := cnt + 1; | ||||||
|  | 			end if; | ||||||
|  | 		end procedure latch; | ||||||
|  | 
 | ||||||
|  | 	begin | ||||||
|  | 		cocotb_rx_a <= '0'; | ||||||
|  | 
 | ||||||
|  | 		loop | ||||||
|  | 			wait on rx_p; | ||||||
|  | 			exit when (rx_p'last_value = '0' or rx_p'last_value = '1'); | ||||||
|  | 		end loop; | ||||||
|  | 
 | ||||||
|  | 		cocotb_rx_a <= '1'; | ||||||
|  | 		wait for 0 ns; | ||||||
|  | 
 | ||||||
|  | 		cnt    := 0; | ||||||
|  | 		bit    := '0'; | ||||||
|  | 		ignore := false; | ||||||
|  | 		first  := true; | ||||||
|  | 		loop | ||||||
|  | 			wait on rx_p for SYM_TIME * 1.5; | ||||||
|  | 			exit when rx_p'last_event > 2.5 * SYM_TIME; | ||||||
|  | 
 | ||||||
|  | 			if rx_p'event then          -- short | ||||||
|  | 				if not ignore then | ||||||
|  | 					latch(bit); | ||||||
|  | 				end if; | ||||||
|  | 				ignore := not ignore; | ||||||
|  | 			else                        -- long | ||||||
|  | 				if (first) then | ||||||
|  | 					bit  := '1'; | ||||||
|  | 					cnt  := 1; | ||||||
|  | 					d(0) := '1'; | ||||||
|  | 				end if; | ||||||
|  | 				bit    := not bit; | ||||||
|  | 				latch(bit); | ||||||
|  | 				ignore := true; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			first := false; | ||||||
|  | 		end loop; | ||||||
|  | 	end process eth_rx_proc; | ||||||
|  | 
 | ||||||
|  | end architecture bench; | ||||||
							
								
								
									
										8
									
								
								cocotb/configure_network.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								cocotb/configure_network.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #!/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 | ||||||
|  | 
 | ||||||
							
								
								
									
										94
									
								
								cocotb/hw_itl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								cocotb/hw_itl.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | import cocotb | ||||||
|  | from cocotb.triggers import * | ||||||
|  | from cocotb.result import * | ||||||
|  | from cocotb.queue import Queue | ||||||
|  | from cocotb_helpers import buffers | ||||||
|  | 
 | ||||||
|  | import sys | ||||||
|  | import threading | ||||||
|  | import socket | ||||||
|  | import time | ||||||
|  | import os | ||||||
|  | import asyncio | ||||||
|  | import fcntl | ||||||
|  | import zlib | ||||||
|  | 
 | ||||||
|  | ''' | ||||||
|  | # 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 | ||||||
|  | ''' | ||||||
|  | 
 | ||||||
|  | class MacDevReceiver(): | ||||||
|  | 	def __init__(self, dut, eth_tx, eth_rx, dev): | ||||||
|  | 		self.dut = dut | ||||||
|  | 		self.eth_tx = eth_tx | ||||||
|  | 		self.eth_rx = eth_rx | ||||||
|  | 		self.dev = dev | ||||||
|  | 		self.mac_rx_ev = Event() | ||||||
|  | 
 | ||||||
|  | 		ETH_P_ALL=3 | ||||||
|  | 		self.macdev=socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) | ||||||
|  | 		self.macdev.bind((dev, 0)) | ||||||
|  | 		fcntl.fcntl(self.macdev, fcntl.F_SETFL, os.O_NONBLOCK) # Not the best way to poll, but I couldn't get asyncio to play nicely with threading... | ||||||
|  | 
 | ||||||
|  | 	def eth_fcs(self, data): | ||||||
|  | 		crc = zlib.crc32(data) & 0xFFFF_FFFF | ||||||
|  | 		return crc.to_bytes(4, byteorder='little') | ||||||
|  | 
 | ||||||
|  | 	async def main(self): | ||||||
|  | 		ETH_HEAD = b'\x55\x55\x55\x55\xD5' | ||||||
|  | 		while True: | ||||||
|  | 			try: | ||||||
|  | 				r = self.macdev.recv(2000) | ||||||
|  | 				if len(r) < 60: | ||||||
|  | 					r += b'\x00' * (60 - len(r)) | ||||||
|  | 				r += self.eth_fcs(r) | ||||||
|  | 				self.dut._log.debug("DUT RX: " + str(r)) | ||||||
|  | 				await self.eth_tx.send(ETH_HEAD + r); | ||||||
|  | 			except: | ||||||
|  | 				await Timer(1, "us") | ||||||
|  | 				pass | ||||||
|  | 	async def main_rx(self): | ||||||
|  | 		while True: | ||||||
|  | 			frame = await self.eth_rx.queue.get() | ||||||
|  | 			try: | ||||||
|  | 				while (frame[0] != 0xD5): | ||||||
|  | 					frame = frame[1:] | ||||||
|  | 				frame = frame[1:] | ||||||
|  | 			except: | ||||||
|  | 				self.dut._log.debug("NLP / Invalid frame") | ||||||
|  | 				continue | ||||||
|  | 			self.dut._log.debug("DUT TX: " + str(frame)) | ||||||
|  | 			self.macdev.send(frame) | ||||||
|  | 
 | ||||||
|  | 	async def start(self): | ||||||
|  | 		await cocotb.start(self.main()) | ||||||
|  | 		await cocotb.start(self.main_rx()) | ||||||
|  | 
 | ||||||
|  | @cocotb.test() | ||||||
|  | async def hwitl(dut): | ||||||
|  | 	"""Real-Ethernet-hardware in the loop test""" | ||||||
|  | 
 | ||||||
|  | 	# Start verification components | ||||||
|  | 	eth_tx = buffers.tx_buffer(dut, "cocovc_eth_inst.cocotb_tx_") | ||||||
|  | 	await eth_tx.start() | ||||||
|  | 	eth_rx = buffers.rx_buffer(dut, "cocovc_eth_inst.cocotb_rx_") | ||||||
|  | 	await eth_rx.start() | ||||||
|  | 
 | ||||||
|  | 	# Start local monitors | ||||||
|  | 	macdev_receiver = MacDevReceiver(dut, eth_tx, eth_rx, "veth2") | ||||||
|  | 
 | ||||||
|  | 	# Wait for VHDL part to be ready | ||||||
|  | 	await Edge(dut.bench_ready) | ||||||
|  | 
 | ||||||
|  | 	print("beep") | ||||||
|  | 	await macdev_receiver.start() | ||||||
|  | 
 | ||||||
|  | 	print("Press Ctrl+C to stop the test.") | ||||||
|  | 	await Timer(100, "sec") | ||||||
							
								
								
									
										5
									
								
								cocotb/hw_itl.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								cocotb/hw_itl.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | echo Hardware in the loop test | ||||||
|  | 
 | ||||||
|  | make TOPLEVEL=cocotb_top_hwitl MODULE=hw_itl | ||||||
							
								
								
									
										7
									
								
								demo/lattice_brevia2/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								demo/lattice_brevia2/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | impl1/ | ||||||
|  | *.ccl | ||||||
|  | .recovery | ||||||
|  | .run_manager.ini | ||||||
|  | .setting.ini | ||||||
|  | .spread_sheet.ini | ||||||
|  | .spreadsheet_view.ini | ||||||
							
								
								
									
										112
									
								
								demo/lattice_brevia2/ip/pll0.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								demo/lattice_brevia2/ip/pll0.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | -- VHDL netlist generated by SCUBA Diamond (64-bit) 3.12.0.240.2 | ||||||
|  | -- Module  Version: 5.7 | ||||||
|  | --/usr/local/diamond/3.12/ispfpga/bin/lin64/scuba -w -n pll0 -lang vhdl -synth lse -arch mg5a00 -type pll -fin 50 -phase_cntl STATIC -fclkop 140 -fclkop_tol 0.0 -fb_mode CLOCKTREE -noclkos -noclkok -norst -noclkok2  | ||||||
|  | 
 | ||||||
|  | -- Sat Jan 22 20:39:11 2022 | ||||||
|  | 
 | ||||||
|  | library IEEE; | ||||||
|  | use IEEE.std_logic_1164.all; | ||||||
|  | -- synopsys translate_off | ||||||
|  | library xp2; | ||||||
|  | use xp2.components.all; | ||||||
|  | -- synopsys translate_on | ||||||
|  | 
 | ||||||
|  | entity pll0 is | ||||||
|  | 	port( | ||||||
|  | 		CLK   : in  std_logic; | ||||||
|  | 		CLKOP : out std_logic; | ||||||
|  | 		LOCK  : out std_logic); | ||||||
|  | end pll0; | ||||||
|  | 
 | ||||||
|  | architecture Structure of pll0 is | ||||||
|  | 
 | ||||||
|  | 	-- internal signal declarations | ||||||
|  | 	signal CLKOP_t   : std_logic; | ||||||
|  | 	signal scuba_vlo : std_logic; | ||||||
|  | 
 | ||||||
|  | 	-- local component declarations | ||||||
|  | 	component VLO | ||||||
|  | 		port(Z : out std_logic); | ||||||
|  | 	end component; | ||||||
|  | 	component EPLLD1 | ||||||
|  | 		-- synopsys translate_off | ||||||
|  | 		generic(CLKOK_BYPASS : in String; CLKOS_BYPASS : in String; | ||||||
|  | 		        CLKOP_BYPASS : in String; DUTY : in Integer; | ||||||
|  | 		        PHASEADJ     : in String; PHASE_CNTL : in String; | ||||||
|  | 		        CLKOK_DIV    : in Integer; CLKFB_DIV : in Integer; | ||||||
|  | 		        CLKOP_DIV    : in Integer; CLKI_DIV : in Integer); | ||||||
|  | 		-- synopsys translate_on | ||||||
|  | 		port(CLKI     : in  std_logic; CLKFB : in std_logic; RST : in std_logic; | ||||||
|  | 		     RSTK     : in  std_logic; DPAMODE : in std_logic; DRPAI3 : in std_logic; | ||||||
|  | 		     DRPAI2   : in  std_logic; DRPAI1 : in std_logic; DRPAI0 : in std_logic; | ||||||
|  | 		     DFPAI3   : in  std_logic; DFPAI2 : in std_logic; DFPAI1 : in std_logic; | ||||||
|  | 		     DFPAI0   : in  std_logic; PWD : in std_logic; CLKOP : out std_logic; | ||||||
|  | 		     CLKOS    : out std_logic; CLKOK : out std_logic; LOCK : out std_logic; | ||||||
|  | 		     CLKINTFB : out std_logic); | ||||||
|  | 	end component; | ||||||
|  | 	attribute CLKOK_BYPASS        : string; | ||||||
|  | 	attribute CLKOS_BYPASS        : string; | ||||||
|  | 	attribute FREQUENCY_PIN_CLKOP : string; | ||||||
|  | 	attribute CLKOP_BYPASS        : string; | ||||||
|  | 	attribute PHASE_CNTL          : string; | ||||||
|  | 	attribute DUTY                : string; | ||||||
|  | 	attribute PHASEADJ            : string; | ||||||
|  | 	attribute FREQUENCY_PIN_CLKI  : string; | ||||||
|  | 	attribute CLKOK_DIV           : string; | ||||||
|  | 	attribute CLKOP_DIV           : string; | ||||||
|  | 	attribute CLKFB_DIV           : string; | ||||||
|  | 	attribute CLKI_DIV            : string; | ||||||
|  | 	attribute FIN                 : string; | ||||||
|  | 	attribute CLKOK_BYPASS of PLLInst_0 : label is "DISABLED"; | ||||||
|  | 	attribute CLKOS_BYPASS of PLLInst_0 : label is "DISABLED"; | ||||||
|  | 	attribute FREQUENCY_PIN_CLKOP of PLLInst_0 : label is "140.000000"; | ||||||
|  | 	attribute CLKOP_BYPASS of PLLInst_0 : label is "DISABLED"; | ||||||
|  | 	attribute PHASE_CNTL of PLLInst_0 : label is "STATIC"; | ||||||
|  | 	attribute DUTY of PLLInst_0 : label is "8"; | ||||||
|  | 	attribute PHASEADJ of PLLInst_0 : label is "0.0"; | ||||||
|  | 	attribute FREQUENCY_PIN_CLKI of PLLInst_0 : label is "50.000000"; | ||||||
|  | 	attribute CLKOK_DIV of PLLInst_0 : label is "2"; | ||||||
|  | 	attribute CLKOP_DIV of PLLInst_0 : label is "4"; | ||||||
|  | 	attribute CLKFB_DIV of PLLInst_0 : label is "14"; | ||||||
|  | 	attribute CLKI_DIV of PLLInst_0 : label is "5"; | ||||||
|  | 	attribute FIN of PLLInst_0 : label is "50.000000"; | ||||||
|  | 	attribute syn_keep            : boolean; | ||||||
|  | 	attribute NGD_DRC_MASK        : integer; | ||||||
|  | 	attribute NGD_DRC_MASK of Structure : architecture is 1; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 	-- component instantiation statements | ||||||
|  | 	scuba_vlo_inst : VLO | ||||||
|  | 		port map(Z => scuba_vlo); | ||||||
|  | 
 | ||||||
|  | 	PLLInst_0 : EPLLD1 | ||||||
|  | 			-- synopsys translate_off | ||||||
|  | 		generic map(CLKOK_BYPASS => "DISABLED", CLKOS_BYPASS => "DISABLED", | ||||||
|  | 		            CLKOP_BYPASS => "DISABLED", PHASE_CNTL => "STATIC", DUTY => 8, | ||||||
|  | 		            PHASEADJ     => "0.0", CLKOK_DIV => 2, CLKOP_DIV => 4, CLKFB_DIV => 14, | ||||||
|  | 		            CLKI_DIV     => 5) | ||||||
|  | 			-- synopsys translate_on | ||||||
|  | 		port map(CLKI     => CLK, CLKFB => CLKOP_t, RST => scuba_vlo, | ||||||
|  | 		         RSTK     => scuba_vlo, DPAMODE => scuba_vlo, DRPAI3 => scuba_vlo, | ||||||
|  | 		         DRPAI2   => scuba_vlo, DRPAI1 => scuba_vlo, DRPAI0 => scuba_vlo, | ||||||
|  | 		         DFPAI3   => scuba_vlo, DFPAI2 => scuba_vlo, DFPAI1 => scuba_vlo, | ||||||
|  | 		         DFPAI0   => scuba_vlo, PWD => scuba_vlo, CLKOP => CLKOP_t, | ||||||
|  | 		         CLKOS    => open, CLKOK => open, LOCK => LOCK, CLKINTFB => open); | ||||||
|  | 
 | ||||||
|  | 	CLKOP <= CLKOP_t; | ||||||
|  | end Structure; | ||||||
|  | 
 | ||||||
|  | -- synopsys translate_off | ||||||
|  | library xp2; | ||||||
|  | configuration Structure_CON of pll0 is | ||||||
|  | 	for Structure | ||||||
|  | 		for all : VLO | ||||||
|  | 			use entity xp2.VLO(V); | ||||||
|  | 		end for; | ||||||
|  | 		for all : EPLLD1 | ||||||
|  | 			use entity xp2.EPLLD1(V); | ||||||
|  | 		end for; | ||||||
|  | 	end for; | ||||||
|  | end Structure_CON; | ||||||
|  | 
 | ||||||
|  | -- synopsys translate_on | ||||||
							
								
								
									
										47
									
								
								demo/lattice_brevia2/mac_test/mac_test.ldf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								demo/lattice_brevia2/mac_test/mac_test.ldf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <BaliProject version="3.2" title="mac_test" device="LFXP2-5E-6TN144C" default_implementation="impl1"> | ||||||
|  |     <Options/> | ||||||
|  |     <Implementation title="impl1" dir="impl1" description="impl1" synthesis="lse" default_strategy="Strategy1"> | ||||||
|  |         <Options def_top="trashernet.trashernet_eth" lib="work" top="top_mac_test"/> | ||||||
|  |         <Source name="../ip/pll0.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../design/top_mac_test.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options top_module="top_mac_test"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../design/ledcon.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/cdc_strobe.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/crc.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/synchronizer.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_eth.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_mac.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_phy.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_phy_cdc.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_pkg.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="impl1/impl1.xcf" type="Programming Project File" type_short="Programming"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="mac_test.lpf" type="Logic Preference" type_short="LPF"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |     </Implementation> | ||||||
|  |     <Strategy name="Strategy1" file="mac_test1.sty"/> | ||||||
|  | </BaliProject> | ||||||
							
								
								
									
										47
									
								
								demo/lattice_brevia2/mac_test/mac_test.lpf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								demo/lattice_brevia2/mac_test/mac_test.lpf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | BLOCK RESETPATHS ; | ||||||
|  | BLOCK ASYNCPATHS ; | ||||||
|  | LOCATE COMP "clk" SITE "21" ; | ||||||
|  | LOCATE COMP "rst_n" SITE "19" ; | ||||||
|  | LOCATE COMP "rx_p" SITE "94" ; | ||||||
|  | LOCATE COMP "led_n[0]" SITE "37" ; | ||||||
|  | LOCATE COMP "led_n[1]" SITE "38" ; | ||||||
|  | LOCATE COMP "led_n[2]" SITE "39" ; | ||||||
|  | LOCATE COMP "led_n[3]" SITE "40" ; | ||||||
|  | LOCATE COMP "led_n[4]" SITE "43" ; | ||||||
|  | LOCATE COMP "led_n[5]" SITE "44" ; | ||||||
|  | LOCATE COMP "led_n[6]" SITE "45" ; | ||||||
|  | LOCATE COMP "led_n[7]" SITE "46" ; | ||||||
|  | IOBUF PORT "rx_p" IO_TYPE=LVPECL33 ; | ||||||
|  | IOBUF PORT "clk" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "rst_n" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[0]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[1]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[2]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[3]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[4]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[5]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[6]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[7]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[0]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[1]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[2]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[3]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[4]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[5]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[6]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[7]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "tx_n" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "tx_p" IO_TYPE=LVCMOS33 ; | ||||||
|  | LOCATE COMP "tx_n" SITE "114" ; | ||||||
|  | LOCATE COMP "tx_p" SITE "116" ; | ||||||
|  | LOCATE COMP "button_n[0]" SITE "54" ; | ||||||
|  | LOCATE COMP "debug_data[0]" SITE "73" ; | ||||||
|  | LOCATE COMP "debug_data[1]" SITE "74" ; | ||||||
|  | LOCATE COMP "debug_data[2]" SITE "77" ; | ||||||
|  | LOCATE COMP "debug_data[3]" SITE "87" ; | ||||||
|  | LOCATE COMP "debug_data[4]" SITE "88" ; | ||||||
|  | LOCATE COMP "debug_data[5]" SITE "89" ; | ||||||
|  | LOCATE COMP "debug_data[6]" SITE "90" ; | ||||||
|  | LOCATE COMP "debug_data[7]" SITE "91" ; | ||||||
|  | BLOCK PATH FROM CLKNET "clk_c" TO CLKNET "clk_phy" ; | ||||||
|  | BLOCK PATH FROM CLKNET "clk_phy" TO CLKNET "clk_c" ; | ||||||
							
								
								
									
										206
									
								
								demo/lattice_brevia2/mac_test/mac_test1.sty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								demo/lattice_brevia2/mac_test/mac_test1.sty
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE strategy> | ||||||
|  | <Strategy version="1.0" predefined="0" description="" label="Strategy1"> | ||||||
|  |     <Property name="PROP_BD_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfHardtimer" value="Enable" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInBusNameConv" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInLibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInRemLoc" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfMemPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_ParSearchPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_AddressBitGen" value="Increment" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_AllowReadBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ByteWideBitMirror" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_CapReadBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ConModBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_CreateBitFile" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DisRAMResBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DisableUESBitgen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DonePinBitGen" value="Pullup" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DoneSigBitGen" value="4" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_EnIOBitGen" value="TriStateDuringReConfig" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_EnIntOscBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ExtClockBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GSREnableBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GSRRelOnBitGen" value="DoneIn" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GranTimBitGen" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_IOTriRelBitGen" value="Cycle 2" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_JTAGEnableBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_LenBitsBitGen" value="24" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_MIFFileBitGen" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_NoHeader" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatBitGen" value="Bit File (Binary)" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatBitGen_REF" value="Bit File (Binary)" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatPromGen" value="Intel Hex 32-bit" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ParityCheckBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ReadBackBitGen" value="Flash" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ReadCaptureBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_RemZeroFramesBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_RunDRCBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SearchPthBitGen" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_StartUpClkBitGen" value="Cclk" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SynchIOBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SysClockConBitGen" value="Reset" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SysConBitGen" value="Reset" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_UFMOrder" value="Normal" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_WaitStTimBitGen" value="5" time="0"/> | ||||||
|  |     <Property name="PROP_IOTIMING_AllSpeed" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_AllowDUPMod" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CarryChain" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CarryChainLength" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DSPStyle" value="DSP" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DSPUtil" value="100" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DecodeUnreachableStates" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DisableDistRam" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EBRUtil" value="100" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfFrequency" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfHardtimer" value="Enable" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfInLibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfInRemLoc" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfMemPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_FIXGATEDCLKS" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_FSMEncodeStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ForceGSRInfer" value="No" time="0"/> | ||||||
|  |     <Property name="PROP_LST_IOInsertion" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_InterFileDump" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_LoopLimit" value="1950" time="0"/> | ||||||
|  |     <Property name="PROP_LST_MaxFanout" value="1000" time="0"/> | ||||||
|  |     <Property name="PROP_LST_MuxStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_NumCriticalPaths" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_OptimizeGoal" value="Timing" time="0"/> | ||||||
|  |     <Property name="PROP_LST_PropagatConst" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_RAMStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ROMStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_RemoveDupRegs" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ResolvedMixedDrivers" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ResourceShare" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_UseIOReg" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_UseLPF" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_VHDL2008" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_AutoTiming" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_CheckUnconstrainedConns" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_CheckUnconstrainedPaths" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_FullName" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_NumUnconstrainedPaths" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_ReportStyle" value="Verbose Timing Report" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_RouteEstAlogtithm" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_RptAsynTimLoop" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_WordCasePaths" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_IgnorePreErr" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MAPIORegister" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MAPInferGSR" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MapModArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_OvermapDevice" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_PackLogMapDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_RegRetiming" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_SigCrossRef" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_SymCrossRef" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDriven" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDrivenNodeRep" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDrivenPack" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_AutoTiming" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_CheckUnconstrainedConns" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_CheckUnconstrainedPaths" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_FullName" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_NumUnconstrainedPaths" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_ReportStyle" value="Verbose Timing Report" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_RptAsynTimLoop" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_SpeedForHoldAnalysis" value="m" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_SpeedForSetupAnalysis" value="default" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_WordCasePaths" value="10" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_CrDlyStFileParDes" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_DisableTDParDes" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_EffortParDes" value="5" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_MultiSeedSortMode" value="Worst Slack" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_NewRouteParDes" value="NBR" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PARClockSkew" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PARModArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_ParMultiNodeList" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PlcIterParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PrefErrorOut" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RemoveDir" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RouteDlyRedParDes" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutePassParDes" value="6" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RouteResOptParDes" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutingCDP" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutingCDR" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RunParWithTrce" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RunTimeReduction" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_SaveBestRsltParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_StopZero" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_parHold" value="On" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_parPathBased" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfArrayBoundsCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfAutoResOfRam" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfClockDomainCross" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfDSPAcrossHie" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfFullCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfIgnoreRamRWCol" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfMissConstraint" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfNetFanout" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfParaCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfReencodeFSM" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfResSharing" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfTimingViolation" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfUseSafeFSM" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfVlog2001" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynComArea" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynCritcal" value="3" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynFSM" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynFreq" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynGSR" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynGatedClk" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynIOPad" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynOutNetForm" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynOutPref" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynRepClkFreq" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynRetime" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynTimSum" value="10" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynTransform" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSyninpd" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynoutd" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ClockConversion" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_DisableRegisterRep" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfAllowDUPMod" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfArea" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfFanout" value="1000" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfFrequency" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfGSR" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfNumStartEnd" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfOutNetForm" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfPushTirstates" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfResSharing" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfRunRetiming" value="Pipelining Only" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfSymFSM" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfUnconsClk" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfVerilogInput" value="Verilog 2001" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ExportSetting" value="No" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_LibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ResolvedMixedDrivers" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_UpdateCompilePtTimData" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_UseLPF" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_VHDL2008" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_THERMAL_DefaultFreq" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_MaxDelSimDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_MinSpeedGrade" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_ModPreSimDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_NegStupHldTim" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimGenPUR" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimGenX" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimHierSep" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TransportModeOfPathDelay" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TrgtSpeedGrade" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_WriteVerboseNetlist" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TMCHK_EnableCheck" value="True" time="0"/> | ||||||
|  | </Strategy> | ||||||
							
								
								
									
										68
									
								
								demo/lattice_brevia2/pingable/pingable.ldf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								demo/lattice_brevia2/pingable/pingable.ldf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <BaliProject version="3.2" title="pingable" device="LFXP2-5E-6TN144C" default_implementation="impl1"> | ||||||
|  |     <Options/> | ||||||
|  |     <Implementation title="impl1" dir="impl1" description="impl1" synthesis="lse" default_strategy="Strategy1"> | ||||||
|  |         <Options def_top="trashernet.fifo" lib="work" top="top_pingable"/> | ||||||
|  |         <Source name="../ip/pll0.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../design/ledcon.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/cdc_strobe.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/crc.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/synchronizer.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_eth.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_mac.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_phy.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_phy_cdc.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_pkg.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_arp.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../design/top_hwitl.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/timer.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../design/top_pingable.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options top_module="top_pingable"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/fifo.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_icmp.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_ipv4.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="../../../trashernet/trashernet_ipv4prot.vhd" type="VHDL" type_short="VHDL"> | ||||||
|  |             <Options lib="trashernet"/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="impl1/impl1.xcf" type="Programming Project File" type_short="Programming"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |         <Source name="pingable.lpf" type="Logic Preference" type_short="LPF"> | ||||||
|  |             <Options/> | ||||||
|  |         </Source> | ||||||
|  |     </Implementation> | ||||||
|  |     <Strategy name="Strategy1" file="pingable1.sty"/> | ||||||
|  | </BaliProject> | ||||||
							
								
								
									
										47
									
								
								demo/lattice_brevia2/pingable/pingable.lpf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								demo/lattice_brevia2/pingable/pingable.lpf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | BLOCK RESETPATHS ; | ||||||
|  | BLOCK ASYNCPATHS ; | ||||||
|  | LOCATE COMP "clk" SITE "21" ; | ||||||
|  | LOCATE COMP "rst_n" SITE "19" ; | ||||||
|  | LOCATE COMP "rx_p" SITE "94" ; | ||||||
|  | LOCATE COMP "led_n[0]" SITE "37" ; | ||||||
|  | LOCATE COMP "led_n[1]" SITE "38" ; | ||||||
|  | LOCATE COMP "led_n[2]" SITE "39" ; | ||||||
|  | LOCATE COMP "led_n[3]" SITE "40" ; | ||||||
|  | LOCATE COMP "led_n[4]" SITE "43" ; | ||||||
|  | LOCATE COMP "led_n[5]" SITE "44" ; | ||||||
|  | LOCATE COMP "led_n[6]" SITE "45" ; | ||||||
|  | LOCATE COMP "led_n[7]" SITE "46" ; | ||||||
|  | IOBUF PORT "rx_p" IO_TYPE=LVPECL33 ; | ||||||
|  | IOBUF PORT "clk" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "rst_n" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[0]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[1]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[2]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[3]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[4]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[5]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[6]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "debug_data[7]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[0]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[1]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[2]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[3]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[4]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[5]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[6]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "led_n[7]" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "tx_n" IO_TYPE=LVCMOS33 ; | ||||||
|  | IOBUF PORT "tx_p" IO_TYPE=LVCMOS33 ; | ||||||
|  | LOCATE COMP "tx_n" SITE "114" ; | ||||||
|  | LOCATE COMP "tx_p" SITE "116" ; | ||||||
|  | LOCATE COMP "button_n[0]" SITE "54" ; | ||||||
|  | LOCATE COMP "debug_data[0]" SITE "73" ; | ||||||
|  | LOCATE COMP "debug_data[1]" SITE "74" ; | ||||||
|  | LOCATE COMP "debug_data[2]" SITE "77" ; | ||||||
|  | LOCATE COMP "debug_data[3]" SITE "87" ; | ||||||
|  | LOCATE COMP "debug_data[4]" SITE "88" ; | ||||||
|  | LOCATE COMP "debug_data[5]" SITE "89" ; | ||||||
|  | LOCATE COMP "debug_data[6]" SITE "90" ; | ||||||
|  | LOCATE COMP "debug_data[7]" SITE "91" ; | ||||||
|  | BLOCK PATH FROM CLKNET "clk_c" TO CLKNET "clk_phy" ; | ||||||
|  | BLOCK PATH FROM CLKNET "clk_phy" TO CLKNET "clk_c" ; | ||||||
							
								
								
									
										206
									
								
								demo/lattice_brevia2/pingable/pingable1.sty
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								demo/lattice_brevia2/pingable/pingable1.sty
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE strategy> | ||||||
|  | <Strategy version="1.0" predefined="0" description="" label="Strategy1"> | ||||||
|  |     <Property name="PROP_BD_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfHardtimer" value="Enable" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInBusNameConv" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInLibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfInRemLoc" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_BD_EdfMemPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BD_ParSearchPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_AddressBitGen" value="Increment" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_AllowReadBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ByteWideBitMirror" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_CapReadBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ConModBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_CreateBitFile" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DisRAMResBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DisableUESBitgen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DonePinBitGen" value="Pullup" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_DoneSigBitGen" value="4" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_EnIOBitGen" value="TriStateDuringReConfig" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_EnIntOscBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ExtClockBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GSREnableBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GSRRelOnBitGen" value="DoneIn" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_GranTimBitGen" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_IOTriRelBitGen" value="Cycle 2" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_JTAGEnableBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_LenBitsBitGen" value="24" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_MIFFileBitGen" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_NoHeader" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatBitGen" value="Bit File (Binary)" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatBitGen_REF" value="Bit File (Binary)" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_OutFormatPromGen" value="Intel Hex 32-bit" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ParityCheckBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ReadBackBitGen" value="Flash" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_ReadCaptureBitGen" value="Disable" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_RemZeroFramesBitGen" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_RunDRCBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SearchPthBitGen" value="" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_StartUpClkBitGen" value="Cclk" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SynchIOBitGen" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SysClockConBitGen" value="Reset" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_SysConBitGen" value="Reset" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_UFMOrder" value="Normal" time="0"/> | ||||||
|  |     <Property name="PROP_BIT_WaitStTimBitGen" value="5" time="0"/> | ||||||
|  |     <Property name="PROP_IOTIMING_AllSpeed" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_AllowDUPMod" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CarryChain" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CarryChainLength" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_LST_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DSPStyle" value="DSP" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DSPUtil" value="100" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DecodeUnreachableStates" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_DisableDistRam" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EBRUtil" value="100" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfFrequency" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfHardtimer" value="Enable" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfInLibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfInRemLoc" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_LST_EdfMemPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_FIXGATEDCLKS" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_FSMEncodeStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ForceGSRInfer" value="No" time="0"/> | ||||||
|  |     <Property name="PROP_LST_IOInsertion" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_InterFileDump" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_LoopLimit" value="1950" time="0"/> | ||||||
|  |     <Property name="PROP_LST_MaxFanout" value="1000" time="0"/> | ||||||
|  |     <Property name="PROP_LST_MuxStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_NumCriticalPaths" value="" time="0"/> | ||||||
|  |     <Property name="PROP_LST_OptimizeGoal" value="Timing" time="0"/> | ||||||
|  |     <Property name="PROP_LST_PropagatConst" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_RAMStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ROMStyle" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_RemoveDupRegs" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ResolvedMixedDrivers" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_LST_ResourceShare" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_UseIOReg" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_LST_UseLPF" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_LST_VHDL2008" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_AutoTiming" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_CheckUnconstrainedConns" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_CheckUnconstrainedPaths" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_FullName" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_NumUnconstrainedPaths" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_ReportStyle" value="Verbose Timing Report" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_RouteEstAlogtithm" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_RptAsynTimLoop" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAPSTA_WordCasePaths" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_IgnorePreErr" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MAPIORegister" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MAPInferGSR" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_MapModArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_OvermapDevice" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_PackLogMapDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_RegRetiming" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_SigCrossRef" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_SymCrossRef" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDriven" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDrivenNodeRep" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_MAP_TimingDrivenPack" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_AutoTiming" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_CheckUnconstrainedConns" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_CheckUnconstrainedPaths" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_FullName" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_NumUnconstrainedPaths" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_ReportStyle" value="Verbose Timing Report" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_RptAsynTimLoop" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_SpeedForHoldAnalysis" value="m" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_SpeedForSetupAnalysis" value="default" time="0"/> | ||||||
|  |     <Property name="PROP_PARSTA_WordCasePaths" value="10" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_CrDlyStFileParDes" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_DisableTDParDes" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_EffortParDes" value="5" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_MultiSeedSortMode" value="Worst Slack" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_NewRouteParDes" value="NBR" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PARClockSkew" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PARModArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_ParMultiNodeList" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PlcIterParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_PrefErrorOut" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RemoveDir" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RouteDlyRedParDes" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutePassParDes" value="6" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RouteResOptParDes" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutingCDP" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RoutingCDR" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RunParWithTrce" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_RunTimeReduction" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_SaveBestRsltParDes" value="1" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_StopZero" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_parHold" value="On" time="0"/> | ||||||
|  |     <Property name="PROP_PAR_parPathBased" value="Off" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfArrayBoundsCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfAutoResOfRam" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfClockDomainCross" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfDSPAcrossHie" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfFullCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfIgnoreRamRWCol" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfMissConstraint" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfNetFanout" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfParaCase" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfReencodeFSM" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfResSharing" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfTimingViolation" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfUseSafeFSM" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_EdfVlog2001" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynComArea" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynCritcal" value="3" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynFSM" value="Auto" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynFreq" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynGSR" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynGatedClk" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynIOPad" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynOutNetForm" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynOutPref" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynRepClkFreq" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynRetime" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynTimSum" value="10" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynTransform" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSyninpd" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_PRE_VSynoutd" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ClockConversion" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_CmdLineArgs" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_DisableRegisterRep" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfAllowDUPMod" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfArea" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfFanout" value="1000" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfFrequency" value="200" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfGSR" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfNumStartEnd" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfOutNetForm" value="None" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfPushTirstates" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfResSharing" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfRunRetiming" value="Pipelining Only" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfSymFSM" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfUnconsClk" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_EdfVerilogInput" value="Verilog 2001" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ExportSetting" value="No" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_LibPath" value="" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_ResolvedMixedDrivers" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_UpdateCompilePtTimData" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_UseLPF" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_SYN_VHDL2008" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_THERMAL_DefaultFreq" value="0" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_MaxDelSimDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_MinSpeedGrade" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_ModPreSimDes" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_NegStupHldTim" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimGenPUR" value="True" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimGenX" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TimSimHierSep" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TransportModeOfPathDelay" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_TrgtSpeedGrade" value="" time="0"/> | ||||||
|  |     <Property name="PROP_TIM_WriteVerboseNetlist" value="False" time="0"/> | ||||||
|  |     <Property name="PROP_TMCHK_EnableCheck" value="True" time="0"/> | ||||||
|  | </Strategy> | ||||||
							
								
								
									
										77
									
								
								design/ledcon.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								design/ledcon.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- ledcon.vhd : Simple debug LED helper | ||||||
|  | --   To blink or not blink. | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | entity ledcon is | ||||||
|  | 	generic( | ||||||
|  | 		F_CLK : integer; | ||||||
|  | 		BLINK : boolean_vector | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		clk   : in  std_logic; | ||||||
|  | 		rst   : in  std_logic; | ||||||
|  | 		sig   : in  std_logic_vector; | ||||||
|  | 		led_n : out std_logic_vector | ||||||
|  | 	); | ||||||
|  | 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; | ||||||
|  | begin | ||||||
|  | 	mstimer : process(clk, rst) is | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 			ms_cnt <= MS_CNT_MAX; | ||||||
|  | 		elsif rising_edge(clk) then | ||||||
|  | 			if (ms_cnt = 0) then | ||||||
|  | 				ms_cnt <= MS_CNT_MAX; | ||||||
|  | 			else | ||||||
|  | 				ms_cnt <= ms_cnt - 1; | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process mstimer; | ||||||
|  | 	ms_tick <= '1' when ms_cnt = 0 else '0'; | ||||||
|  | 
 | ||||||
|  | 	ledcongen : for i in sig'low to sig'high generate | ||||||
|  | 		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; | ||||||
|  | 		begin | ||||||
|  | 			ledgen : if BLINK(i) generate | ||||||
|  | 				ledproc : process(clk, rst) is | ||||||
|  | 				begin | ||||||
|  | 					if rst then | ||||||
|  | 						led_cnt <= LED_CNT_MIN; | ||||||
|  | 					elsif rising_edge(clk) then | ||||||
|  | 						if (led_cnt = LED_CNT_MIN) then | ||||||
|  | 							if sig(i) then | ||||||
|  | 								led_cnt <= LED_CNT_MAX; | ||||||
|  | 							end if; | ||||||
|  | 						elsif ms_tick then | ||||||
|  | 							led_cnt <= led_cnt - 1; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 				end process ledproc; | ||||||
|  | 				led_n(i) <= '0' when led_cnt > 0 else '1'; | ||||||
|  | 			else generate | ||||||
|  | 				led_n(i) <= not sig(i); | ||||||
|  | 			end generate ledgen; | ||||||
|  | 		end block ledblock; | ||||||
|  | 	end generate ledcongen; | ||||||
|  | 
 | ||||||
|  | end architecture RTL; | ||||||
							
								
								
									
										496
									
								
								design/top_hwitl.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										496
									
								
								design/top_hwitl.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,496 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- top_hwitl.vhd: Top level design for hardware-in-the-loop tests | ||||||
|  | -- | ||||||
|  | --   Target: Simulation | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | library trashernet; | ||||||
|  | use trashernet.trashernet_pkg.all; | ||||||
|  | 
 | ||||||
|  | entity top_hwitl is | ||||||
|  | 	port( | ||||||
|  | 		clk        : in  std_logic; | ||||||
|  | 		rst_n      : in  std_logic; | ||||||
|  | 		rx_p       : in  std_logic; | ||||||
|  | 		tx_p       : out std_logic; | ||||||
|  | 		tx_n       : out std_logic; | ||||||
|  | 		led_n      : out std_logic_vector(7 downto 0); -- @suppress: Used in different architectures | ||||||
|  | 		button_n   : in  std_logic_vector(3 downto 0); -- @suppress: Used in different architectures | ||||||
|  | 		debug_data : out std_logic_vector(7 downto 0) -- @suppress: Used in different architectures | ||||||
|  | 	); | ||||||
|  | end entity top_hwitl; | ||||||
|  | 
 | ||||||
|  | architecture mac of top_hwitl is | ||||||
|  | 	component pll0 | ||||||
|  | 		port( | ||||||
|  | 			CLK   : in  std_logic; | ||||||
|  | 			CLKOP : out std_logic; | ||||||
|  | 			LOCK  : out std_logic | ||||||
|  | 		); | ||||||
|  | 	end component pll0; | ||||||
|  | 
 | ||||||
|  | 	constant F_CLK     : integer := 50000000; | ||||||
|  | 	constant F_CLK_PHY : integer := 140000000; | ||||||
|  | 
 | ||||||
|  | 	constant ETH_CONFIG : configuration_t := ( | ||||||
|  | 		mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33") | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	signal rst     : std_logic; | ||||||
|  | 	signal clk_phy : std_logic; | ||||||
|  | 
 | ||||||
|  | 	signal phy_out : phy_out_t; | ||||||
|  | 	signal phy_in  : phy_in_t; | ||||||
|  | 
 | ||||||
|  | 	signal mac_out : mac_out_t; | ||||||
|  | 	signal mac_in  : mac_in_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; | ||||||
|  | 
 | ||||||
|  | 	constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1; | ||||||
|  | 	signal   tmo     : integer range 0 to TMO_MAX; | ||||||
|  | begin | ||||||
|  | 	trashernet_mac_inst : entity trashernet.trashernet_mac | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			mac_out => mac_out, | ||||||
|  | 			mac_in  => mac_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	pll0_inst : pll0 | ||||||
|  | 		port map( | ||||||
|  | 			CLK   => clk, | ||||||
|  | 			CLKOP => clk_phy, | ||||||
|  | 			LOCK  => open | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK     => F_CLK, | ||||||
|  | 			F_CLK_PHY => F_CLK_PHY | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			phy_clk => clk_phy, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			rx_p    => rx_p, | ||||||
|  | 			tx_p    => tx_p, | ||||||
|  | 			tx_n    => tx_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	reset_sync : process(clk, rst_n) is | ||||||
|  | 	begin | ||||||
|  | 		if (rst_n = '0') then | ||||||
|  | 			rst <= '1'; | ||||||
|  | 		elsif (rising_edge(clk)) then | ||||||
|  | 			rst <= '0'; | ||||||
|  | 		end if; | ||||||
|  | 	end process reset_sync; | ||||||
|  | 
 | ||||||
|  | 	receiver : process(clk, rst) is | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 			state                 <= IDLE; | ||||||
|  | 			mac_in.tx_mac_data_en <= '0'; | ||||||
|  | 			tmo                   <= 0; | ||||||
|  | 		elsif rising_edge(clk) then | ||||||
|  | 			if (tmo /= 0) then | ||||||
|  | 				tmo <= tmo - 1; | ||||||
|  | 			end if; | ||||||
|  | 			case state is | ||||||
|  | 				when IDLE => | ||||||
|  | 					if mac_out.rx_mac_crc_ok then | ||||||
|  | 						if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts! | ||||||
|  | 							state                            <= TX; | ||||||
|  | 							byte_cnt                         <= BYTE_CNT_MAX; | ||||||
|  | 							mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 
 | ||||||
|  | 					if tmo = 0 then | ||||||
|  | 						state                            <= TX; | ||||||
|  | 						byte_cnt                         <= BYTE_CNT_MAX; | ||||||
|  | 						mac_in.tx_header.mac_destination <= (others => x"FF"); | ||||||
|  | 					end if; | ||||||
|  | 				when TX => | ||||||
|  | 					tmo                            <= TMO_MAX; | ||||||
|  | 					mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8))); | ||||||
|  | 					mac_in.tx_mac_data_en          <= '1'; | ||||||
|  | 					mac_in.tx_mac_data             <= std_logic_vector(to_unsigned(byte_cnt, 8)); | ||||||
|  | 					if mac_out.tx_mac_data_ack then | ||||||
|  | 						if byte_cnt = 1 then | ||||||
|  | 							mac_in.tx_mac_data_en <= '0'; | ||||||
|  | 							state                 <= IDLE; | ||||||
|  | 						else | ||||||
|  | 							byte_cnt <= byte_cnt - 1; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 			end case; | ||||||
|  | 
 | ||||||
|  | 		end if; | ||||||
|  | 	end process receiver; | ||||||
|  | 
 | ||||||
|  | 	mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address; | ||||||
|  | 
 | ||||||
|  | 	debug_data(0) <= tx_p; | ||||||
|  | 	debug_data(1) <= tx_n; | ||||||
|  | 
 | ||||||
|  | end architecture mac; | ||||||
|  | 
 | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- top_hwitl (eth) | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | 
 | ||||||
|  | library ieee; | ||||||
|  | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
|  | 
 | ||||||
|  | use ieee.math_real.all; | ||||||
|  | 
 | ||||||
|  | library trashernet; | ||||||
|  | use trashernet.trashernet_pkg.all; | ||||||
|  | 
 | ||||||
|  | architecture eth of top_hwitl is | ||||||
|  | 	component pll0 | ||||||
|  | 		port( | ||||||
|  | 			CLK   : in  std_logic; | ||||||
|  | 			CLKOP : out std_logic; | ||||||
|  | 			LOCK  : out std_logic | ||||||
|  | 		); | ||||||
|  | 	end component pll0; | ||||||
|  | 
 | ||||||
|  | 	constant F_CLK     : integer := 50000000; | ||||||
|  | 	constant F_CLK_PHY : integer := 140000000; | ||||||
|  | 
 | ||||||
|  | 	constant ETH_CONFIG : configuration_t      := ( | ||||||
|  | 		mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"33") | ||||||
|  | 	); | ||||||
|  | 	constant IP_CONFIG  : ipv4_configuration_t := ( | ||||||
|  | 		ip_address  => (x"C0", x"A8", x"02", x"02"), | ||||||
|  | 		subnet_mask => (x"FF", x"FF", x"FF", x"00"), | ||||||
|  | 		gateway     => (x"C0", x"A8", x"02", x"01") | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	signal rst     : std_logic; | ||||||
|  | 	signal clk_phy : std_logic; | ||||||
|  | 
 | ||||||
|  | 	signal phy_out : phy_out_t; | ||||||
|  | 	signal phy_in  : phy_in_t; | ||||||
|  | 
 | ||||||
|  | 	signal mac_out : mac_out_t; | ||||||
|  | 	signal mac_in  : mac_in_t; | ||||||
|  | 
 | ||||||
|  | 	constant PROT_ARP              : integer                     := 0; | ||||||
|  | 	constant PROT_IPV4             : integer                     := 1; | ||||||
|  | 	constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ( | ||||||
|  | 		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 arp_out : arp_out_t; | ||||||
|  | 	signal arp_in  : arp_in_t; | ||||||
|  | 
 | ||||||
|  | 	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 := ( | ||||||
|  | 		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); | ||||||
|  | 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, | ||||||
|  | 			rst               => rst, | ||||||
|  | 			ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP), | ||||||
|  | 			ipv4_protocol_in  => ipv4_protocol_in(IPROT_ICMP) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot | ||||||
|  | 		generic map( | ||||||
|  | 			IPV4_PROTOCOLS => IPV4_PROTOCOLS | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk               => clk, | ||||||
|  | 			rst               => rst, | ||||||
|  | 			ipv4_out          => ipv4_out, | ||||||
|  | 			ipv4_in           => ipv4_in, | ||||||
|  | 			ipv4_protocol_out => ipv4_protocol_out, | ||||||
|  | 			ipv4_protocol_in  => ipv4_protocol_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_ipv4_inst : entity trashernet.trashernet_ipv4 | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			ipv4_config     => IP_CONFIG, | ||||||
|  | 			arp_out         => arp_out, | ||||||
|  | 			arp_in          => arp_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out(PROT_IPV4), | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in(PROT_IPV4), | ||||||
|  | 			ipv4_out        => ipv4_out, | ||||||
|  | 			ipv4_in         => ipv4_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_arp_inst : entity trashernet.trashernet_arp | ||||||
|  | 		generic map( | ||||||
|  | 			SYSTICK_FREQ => F_CLK | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			systick         => '1', | ||||||
|  | 			mac_config      => ETH_CONFIG, | ||||||
|  | 			ip_config       => IP_CONFIG, | ||||||
|  | 			arp_out         => arp_out, | ||||||
|  | 			arp_in          => arp_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out(PROT_ARP), | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in(PROT_ARP) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	ethernet_i_in <= ethernet_i_in_t'( | ||||||
|  | 		tx_mac_address => (others => (others => '-')), | ||||||
|  | 		tx_data        => (others => '-'), | ||||||
|  | 		tx_en          => '0', | ||||||
|  | 		tx_length      => (others => '-') | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	trashernet_eth_inst : entity trashernet.trashernet_eth | ||||||
|  | 		generic map( | ||||||
|  | 			ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			mac_out         => mac_out, | ||||||
|  | 			mac_in          => mac_in, | ||||||
|  | 			config          => ETH_CONFIG, | ||||||
|  | 			ethernet_i_out  => ethernet_i_out, | ||||||
|  | 			ethernet_i_in   => ethernet_i_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out, | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_mac_inst : entity trashernet.trashernet_mac | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			mac_out => mac_out, | ||||||
|  | 			mac_in  => mac_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	pll0_inst : pll0 | ||||||
|  | 		port map( | ||||||
|  | 			CLK   => clk, | ||||||
|  | 			CLKOP => clk_phy, | ||||||
|  | 			LOCK  => open | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK     => F_CLK, | ||||||
|  | 			F_CLK_PHY => F_CLK_PHY | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			phy_clk => clk_phy, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			rx_p    => rx_p, | ||||||
|  | 			tx_p    => tx_p, | ||||||
|  | 			tx_n    => tx_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	reset_sync : process(clk, rst_n) is | ||||||
|  | 	begin | ||||||
|  | 		if (rst_n = '0') then | ||||||
|  | 			rst <= '1'; | ||||||
|  | 		elsif (rising_edge(clk)) then | ||||||
|  | 			rst <= '0'; | ||||||
|  | 		end if; | ||||||
|  | 	end process reset_sync; | ||||||
|  | 
 | ||||||
|  | 	receiver : process(clk, rst) is | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 
 | ||||||
|  | 		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)); | ||||||
|  | 			end if; | ||||||
|  | 			if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then | ||||||
|  | 				report "RX ARP"; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			if ipv4_out.rx_ok_stb then | ||||||
|  | 				report "RX IP OK"; | ||||||
|  | 			end if; | ||||||
|  | 			if ipv4_out.rx_error_stb then | ||||||
|  | 				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); | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process receiver; | ||||||
|  | 
 | ||||||
|  | 	udp : process(clk) is | ||||||
|  | 	begin | ||||||
|  | 		if rising_edge(clk) then | ||||||
|  | 			if ipv4_protocol_out(IPROT_UDP).rx_header_rcv then | ||||||
|  | 				report "Receive UDP"; | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process udp; | ||||||
|  | 
 | ||||||
|  | 	tx_udp_p : block | ||||||
|  | 		type   state_t is (IDLE, TX, DONE); | ||||||
|  | 		signal state   : state_t; | ||||||
|  | 
 | ||||||
|  | 		constant PACKET : byte_vector := ( | ||||||
|  | 			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 | ||||||
|  | 				state <= IDLE; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				case state is | ||||||
|  | 					when IDLE => | ||||||
|  | 						if button_n(0) = '0' then | ||||||
|  | 							state <= TX; | ||||||
|  | 							sr    <= PACKET; | ||||||
|  | 							cnt   <= sr'length - 1; | ||||||
|  | 							report "UDP(0): Start TX"; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when TX => | ||||||
|  | 						if udpprot_tx_out(0).tx_data_ack then | ||||||
|  | 							report "UDP(0): byte ack"; | ||||||
|  | 							sr <= sr(sr'low + 1 to sr'high) & x"00"; | ||||||
|  | 							if cnt = 0 then | ||||||
|  | 								state <= DONE; | ||||||
|  | 							else | ||||||
|  | 								cnt <= cnt - 1; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 						if udpprot_tx_out(0).tx_err_stb then | ||||||
|  | 							report "UDP(0): 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?"; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when DONE => | ||||||
|  | 						null; | ||||||
|  | 
 | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process tx_udp; | ||||||
|  | 	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 | ||||||
|  | 			if ipv4_protocol_out(IPROT_ICMP).rx_header_rcv then | ||||||
|  | 				report "Receive ICMP"; | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process icmp; | ||||||
|  | 
 | ||||||
|  | end architecture eth; | ||||||
							
								
								
									
										208
									
								
								design/top_mac_test.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								design/top_mac_test.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,208 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- top_mac_test.vhd: Test design to verify the functionality of the MAC core | ||||||
|  | --   Sends a broadcast with 100 decrementing numbers every 250 ms or when a | ||||||
|  | --   button is pushed. It will also directly reply with that content to any | ||||||
|  | --   packet addressed to us. | ||||||
|  | -- | ||||||
|  | --   Target: Lattice Brevia 2 (LFXP2-5E) | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | library trashernet; | ||||||
|  | use trashernet.trashernet_pkg.all; | ||||||
|  | 
 | ||||||
|  | entity top_mac_test is | ||||||
|  | 	port( | ||||||
|  | 		clk        : in  std_logic; | ||||||
|  | 		rst_n      : in  std_logic; | ||||||
|  | 		rx_p       : in  std_logic; | ||||||
|  | 		tx_p       : out std_logic; | ||||||
|  | 		tx_n       : out std_logic; | ||||||
|  | 		led_n      : out std_logic_vector(7 downto 0); | ||||||
|  | 		button_n   : in  std_logic_vector(3 downto 0); | ||||||
|  | 		debug_data : out std_logic_vector(7 downto 0) | ||||||
|  | 	); | ||||||
|  | end entity top_mac_test; | ||||||
|  | 
 | ||||||
|  | architecture rtl of top_mac_test is | ||||||
|  | 	component pll0 | ||||||
|  | 		port( | ||||||
|  | 			CLK   : in  std_logic; | ||||||
|  | 			CLKOP : out std_logic; | ||||||
|  | 			LOCK  : out std_logic | ||||||
|  | 		); | ||||||
|  | 	end component pll0; | ||||||
|  | 
 | ||||||
|  | 	constant F_CLK     : integer := 50000000; | ||||||
|  | 	constant F_CLK_PHY : integer := 140000000; | ||||||
|  | 
 | ||||||
|  | 	constant LED_BLINK : boolean_vector(led_n'range) := ( | ||||||
|  | 		6 downto 2 => true, | ||||||
|  | 		others     => false | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	constant ETH_CONFIG : configuration_t := ( | ||||||
|  | 		mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33") | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	signal rst          : std_logic; | ||||||
|  | 	signal clk_phy      : std_logic; | ||||||
|  | 	signal phy_pll_lock : std_logic; | ||||||
|  | 
 | ||||||
|  | 	signal led_sig : std_logic_vector(led_n'range); | ||||||
|  | 
 | ||||||
|  | 	signal phy_out : phy_out_t; | ||||||
|  | 	signal phy_in  : phy_in_t; | ||||||
|  | 
 | ||||||
|  | 	signal mac_out : mac_out_t; | ||||||
|  | 	signal mac_in  : mac_in_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 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; | ||||||
|  | begin | ||||||
|  | 	trashernet_mac_inst : entity trashernet.trashernet_mac | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			mac_out => mac_out, | ||||||
|  | 			mac_in  => mac_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	pll0_inst : pll0 | ||||||
|  | 		port map( | ||||||
|  | 			CLK   => clk, | ||||||
|  | 			CLKOP => clk_phy, | ||||||
|  | 			LOCK  => phy_pll_lock | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK     => F_CLK, | ||||||
|  | 			F_CLK_PHY => F_CLK_PHY | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			phy_clk => clk_phy, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			rx_p    => rx_p, | ||||||
|  | 			tx_p    => tx_p, | ||||||
|  | 			tx_n    => tx_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	reset_sync : process(clk, rst_n) is | ||||||
|  | 	begin | ||||||
|  | 		if (rst_n = '0') then | ||||||
|  | 			rst <= '1'; | ||||||
|  | 		elsif (rising_edge(clk)) then | ||||||
|  | 			rst <= '0'; | ||||||
|  | 		end if; | ||||||
|  | 	end process reset_sync; | ||||||
|  | 
 | ||||||
|  | 	ledcon_inst : entity work.ledcon | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK => F_CLK, | ||||||
|  | 			BLINK => LED_BLINK | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk   => clk, | ||||||
|  | 			rst   => rst, | ||||||
|  | 			sig   => led_sig, | ||||||
|  | 			led_n => led_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	led_sig <= ( | ||||||
|  | 		0 => phy_pll_lock, | ||||||
|  | 		1 => phy_out.carrier_detect, | ||||||
|  | 		2 => phy_out.rx_active, | ||||||
|  | 		3 => phy_out.rx_error, | ||||||
|  | 		4 => mac_out.rx_mac_crc_ok, | ||||||
|  | 		5 => mac_out.rx_mac_crc_error, | ||||||
|  | 		6 => mac_out.tx_active, | ||||||
|  | 		7 => '0' | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	synchronizer_inst : entity trashernet.synchronizer | ||||||
|  | 		generic map( | ||||||
|  | 			SIZE => 2 | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk      => clk, | ||||||
|  | 			rst      => rst, | ||||||
|  | 			data_in  => button_n(0), | ||||||
|  | 			data_out => button_n_sync(0) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	button <= not button_n_sync; | ||||||
|  | 
 | ||||||
|  | 	receiver : process(clk, rst) is | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 			state                 <= IDLE; | ||||||
|  | 			mac_in.tx_mac_data_en <= '0'; | ||||||
|  | 			tmo                   <= 0; | ||||||
|  | 		elsif rising_edge(clk) then | ||||||
|  | 			if (tmo /= 0) then | ||||||
|  | 				tmo <= tmo - 1; | ||||||
|  | 			end if; | ||||||
|  | 			case state is | ||||||
|  | 				when IDLE => | ||||||
|  | 					if mac_out.rx_mac_crc_ok then | ||||||
|  | 						if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts! | ||||||
|  | 							state                            <= TX; | ||||||
|  | 							byte_cnt                         <= BYTE_CNT_MAX; | ||||||
|  | 							mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 
 | ||||||
|  | 					if (tmo = 0) or (button(0) = '1') then | ||||||
|  | 						state                            <= TX; | ||||||
|  | 						byte_cnt                         <= BYTE_CNT_MAX; | ||||||
|  | 						mac_in.tx_header.mac_destination <= (others => x"FF"); | ||||||
|  | 					end if; | ||||||
|  | 				when TX => | ||||||
|  | 					tmo                            <= TMO_MAX; | ||||||
|  | 					mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8))); | ||||||
|  | 					mac_in.tx_mac_data_en          <= '1'; | ||||||
|  | 					mac_in.tx_mac_data             <= std_logic_vector(to_unsigned(byte_cnt, 8)); | ||||||
|  | 					if mac_out.tx_mac_data_ack then | ||||||
|  | 						if byte_cnt = 1 then | ||||||
|  | 							mac_in.tx_mac_data_en <= '0'; | ||||||
|  | 							state                 <= IDLE; | ||||||
|  | 						else | ||||||
|  | 							byte_cnt <= byte_cnt - 1; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 			end case; | ||||||
|  | 
 | ||||||
|  | 		end if; | ||||||
|  | 	end process receiver; | ||||||
|  | 
 | ||||||
|  | 	mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address; | ||||||
|  | 
 | ||||||
|  | 	debug_data(0) <= tx_p; | ||||||
|  | 	debug_data(1) <= tx_n; | ||||||
|  | 
 | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										248
									
								
								design/top_pingable.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								design/top_pingable.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,248 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- top_mac_test.vhd: Test design to verify the functionality of the MAC core | ||||||
|  | --   Sends a broadcast with 100 decrementing numbers every 250 ms or when a | ||||||
|  | --   button is pushed. It will also directly reply with that content to any | ||||||
|  | --   packet addressed to us. | ||||||
|  | -- | ||||||
|  | --   Target: Lattice Brevia 2 (LFXP2-5E) | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | library trashernet; | ||||||
|  | use trashernet.trashernet_pkg.all; | ||||||
|  | 
 | ||||||
|  | entity top_pingable is | ||||||
|  | 	port( | ||||||
|  | 		clk        : in  std_logic; | ||||||
|  | 		rst_n      : in  std_logic; | ||||||
|  | 		rx_p       : in  std_logic; | ||||||
|  | 		tx_p       : out std_logic; | ||||||
|  | 		tx_n       : out std_logic; | ||||||
|  | 		led_n      : out std_logic_vector(7 downto 0); | ||||||
|  | 		button_n   : in  std_logic_vector(3 downto 0); | ||||||
|  | 		debug_data : out std_logic_vector(7 downto 0) | ||||||
|  | 	); | ||||||
|  | end entity top_pingable; | ||||||
|  | 
 | ||||||
|  | architecture rtl of top_pingable is | ||||||
|  | 	component pll0 | ||||||
|  | 		port( | ||||||
|  | 			CLK   : in  std_logic; | ||||||
|  | 			CLKOP : out std_logic; | ||||||
|  | 			LOCK  : out std_logic | ||||||
|  | 		); | ||||||
|  | 	end component pll0; | ||||||
|  | 
 | ||||||
|  | 	constant F_CLK     : integer := 50000000; | ||||||
|  | 	constant F_CLK_PHY : integer := 140000000; | ||||||
|  | 
 | ||||||
|  | 	constant LED_BLINK : boolean_vector(led_n'range) := ( | ||||||
|  | 		6 downto 2 => true, | ||||||
|  | 		others     => false | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	constant ETH_CONFIG : configuration_t      := ( | ||||||
|  | 		mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"44") | ||||||
|  | 	); | ||||||
|  | 	constant IP_CONFIG  : ipv4_configuration_t := ( | ||||||
|  | 		ip_address  => (x"C0", x"A8", x"02", x"02"), | ||||||
|  | 		subnet_mask => (x"FF", x"FF", x"FF", x"00"), | ||||||
|  | 		gateway     => (x"C0", x"A8", x"02", x"01") | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	signal rst          : std_logic; | ||||||
|  | 	signal clk_phy      : std_logic; | ||||||
|  | 	signal phy_pll_lock : std_logic; | ||||||
|  | 
 | ||||||
|  | 	signal led_sig : std_logic_vector(led_n'range); | ||||||
|  | 
 | ||||||
|  | 	signal phy_out : phy_out_t; | ||||||
|  | 	signal phy_in  : phy_in_t; | ||||||
|  | 
 | ||||||
|  | 	signal mac_out : mac_out_t; | ||||||
|  | 	signal mac_in  : mac_in_t; | ||||||
|  | 
 | ||||||
|  | 	constant PROT_ARP              : integer                     := 0; | ||||||
|  | 	constant PROT_IPV4             : integer                     := 1; | ||||||
|  | 	constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ( | ||||||
|  | 		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 arp_out : arp_out_t; | ||||||
|  | 	signal arp_in  : arp_in_t; | ||||||
|  | 
 | ||||||
|  | 	signal ipv4_out : ipv4_out_t; | ||||||
|  | 	signal ipv4_in  : ipv4_in_t; | ||||||
|  | 
 | ||||||
|  | 	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 button_n_sync : std_logic_vector(button_n'range); | ||||||
|  | 	signal button        : std_logic_vector(button_n'range); | ||||||
|  | begin | ||||||
|  | 	trashernet_icmp_inst : entity trashernet.trashernet_icmp | ||||||
|  | 		port map( | ||||||
|  | 			clk               => clk, | ||||||
|  | 			rst               => rst, | ||||||
|  | 			ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP), | ||||||
|  | 			ipv4_protocol_in  => ipv4_protocol_in(IPROT_ICMP) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot | ||||||
|  | 		generic map( | ||||||
|  | 			IPV4_PROTOCOLS => IPV4_PROTOCOLS | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk               => clk, | ||||||
|  | 			rst               => rst, | ||||||
|  | 			ipv4_out          => ipv4_out, | ||||||
|  | 			ipv4_in           => ipv4_in, | ||||||
|  | 			ipv4_protocol_out => ipv4_protocol_out, | ||||||
|  | 			ipv4_protocol_in  => ipv4_protocol_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_ipv4_inst : entity trashernet.trashernet_ipv4 | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			ipv4_config     => IP_CONFIG, | ||||||
|  | 			arp_out         => arp_out, | ||||||
|  | 			arp_in          => arp_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out(PROT_IPV4), | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in(PROT_IPV4), | ||||||
|  | 			ipv4_out        => ipv4_out, | ||||||
|  | 			ipv4_in         => ipv4_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_arp_inst : entity trashernet.trashernet_arp | ||||||
|  | 		generic map( | ||||||
|  | 			SYSTICK_FREQ => F_CLK | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			systick         => '1', | ||||||
|  | 			mac_config      => ETH_CONFIG, | ||||||
|  | 			ip_config       => IP_CONFIG, | ||||||
|  | 			arp_out         => arp_out, | ||||||
|  | 			arp_in          => arp_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out(PROT_ARP), | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in(PROT_ARP) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	ethernet_i_in <= ETHERNET_I_IN_UNUSED; | ||||||
|  | 
 | ||||||
|  | 	trashernet_eth_inst : entity trashernet.trashernet_eth | ||||||
|  | 		generic map( | ||||||
|  | 			ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk             => clk, | ||||||
|  | 			rst             => rst, | ||||||
|  | 			mac_out         => mac_out, | ||||||
|  | 			mac_in          => mac_in, | ||||||
|  | 			config          => ETH_CONFIG, | ||||||
|  | 			ethernet_i_out  => ethernet_i_out, | ||||||
|  | 			ethernet_i_in   => ethernet_i_in, | ||||||
|  | 			ethernet_ii_out => ethernet_ii_out, | ||||||
|  | 			ethernet_ii_in  => ethernet_ii_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_mac_inst : entity trashernet.trashernet_mac | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			mac_out => mac_out, | ||||||
|  | 			mac_in  => mac_in | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	pll0_inst : pll0 | ||||||
|  | 		port map( | ||||||
|  | 			CLK   => clk, | ||||||
|  | 			CLKOP => clk_phy, | ||||||
|  | 			LOCK  => phy_pll_lock | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK     => F_CLK, | ||||||
|  | 			F_CLK_PHY => F_CLK_PHY | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk     => clk, | ||||||
|  | 			phy_clk => clk_phy, | ||||||
|  | 			rst     => rst, | ||||||
|  | 			phy_out => phy_out, | ||||||
|  | 			phy_in  => phy_in, | ||||||
|  | 			rx_p    => rx_p, | ||||||
|  | 			tx_p    => tx_p, | ||||||
|  | 			tx_n    => tx_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	reset_sync : process(clk, rst_n) is | ||||||
|  | 	begin | ||||||
|  | 		if (rst_n = '0') then | ||||||
|  | 			rst <= '1'; | ||||||
|  | 		elsif (rising_edge(clk)) then | ||||||
|  | 			rst <= '0'; | ||||||
|  | 		end if; | ||||||
|  | 	end process reset_sync; | ||||||
|  | 
 | ||||||
|  | 	ledcon_inst : entity work.ledcon | ||||||
|  | 		generic map( | ||||||
|  | 			F_CLK => F_CLK, | ||||||
|  | 			BLINK => LED_BLINK | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk   => clk, | ||||||
|  | 			rst   => rst, | ||||||
|  | 			sig   => led_sig, | ||||||
|  | 			led_n => led_n | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	led_sig <= ( | ||||||
|  | 		0 => phy_pll_lock, | ||||||
|  | 		1 => phy_out.carrier_detect, | ||||||
|  | 		2 => phy_out.rx_active, | ||||||
|  | 		3 => phy_out.rx_error, | ||||||
|  | 		4 => mac_out.rx_mac_crc_ok, | ||||||
|  | 		5 => mac_out.rx_mac_crc_error, | ||||||
|  | 		6 => mac_out.tx_active, | ||||||
|  | 		7 => '0' | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	synchronizer_inst : entity trashernet.synchronizer | ||||||
|  | 		generic map( | ||||||
|  | 			SIZE => 2 | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk      => clk, | ||||||
|  | 			rst      => rst, | ||||||
|  | 			data_in  => button_n(0), | ||||||
|  | 			data_out => button_n_sync(0) | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	button <= not button_n_sync; | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										3039
									
								
								doc/circuit.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3039
									
								
								doc/circuit.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| After Width: | Height: | Size: 229 KiB | 
							
								
								
									
										2016
									
								
								doc/hardware.kicad_sch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2016
									
								
								doc/hardware.kicad_sch
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | vunit_hdl | ||||||
|  | cocotb | ||||||
							
								
								
									
										16
									
								
								run.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								run.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | from vunit import VUnit | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | library_names = ["trashernet", "bench"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | libs = {} | ||||||
|  | 
 | ||||||
|  | vu = VUnit.from_argv() | ||||||
|  | 
 | ||||||
|  | for library_name in library_names: | ||||||
|  | 	libs[library_name] = vu.add_library(library_name) | ||||||
|  | 	libs[library_name].add_source_files(os.path.join(library_name, "*.vhd")) | ||||||
|  | 
 | ||||||
|  | vu.main() | ||||||
| @ -54,8 +54,7 @@ begin | |||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 	-- strobify | 	-- strobify | ||||||
| 	b_toggle_last <= '0' when b_rst | 	b_toggle_last <= '0' when b_rst else | ||||||
| 	                 else b_toggle when rising_edge(b_clk) | 	                 b_toggle when rising_edge(b_clk); | ||||||
| 	; |  | ||||||
| 	b_out         <= (b_toggle ?/= b_toggle_last); | 	b_out         <= (b_toggle ?/= b_toggle_last); | ||||||
| end architecture rtl; | end architecture rtl; | ||||||
|  | |||||||
							
								
								
									
										98
									
								
								trashernet/fifo.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								trashernet/fifo.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- fifo.vhd : Basic single-clock FIFO | ||||||
|  | --   Implements a basic single-clock FIFO | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | entity fifo is | ||||||
|  | 	generic( | ||||||
|  | 		DEPTH : natural                 -- Number of elements the FIFO can hold | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk      : in  std_logic;       -- Global clock | ||||||
|  | 		rst      : in  std_logic;       -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		-- FIFO | ||||||
|  | 		clear    : in  std_logic;       -- Synchronous reset (clear FIFO) | ||||||
|  | 		data_in  : in  std_logic_vector; -- Data into the FIFO (automatically constrains width) | ||||||
|  | 		push     : in  std_logic;       -- Push `data_in` into the FIFO | ||||||
|  | 		full     : out std_logic;       -- No further elements can be pushed into the FIFO | ||||||
|  | 		data_out : out std_logic_vector; -- Data out of the FIFO | ||||||
|  | 		pop      : in  std_logic;       -- Get an element from the FIFO | ||||||
|  | 		empty    : out std_logic        -- FIFO is empty | ||||||
|  | 	); | ||||||
|  | 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); | ||||||
|  | 
 | ||||||
|  | 	subtype memory_pointer_t is integer range 0 to DEPTH - 1; | ||||||
|  | 	subtype usage_counter_t  is integer range 0 to DEPTH; | ||||||
|  | 
 | ||||||
|  | 	signal read_pointer  : memory_pointer_t; | ||||||
|  | 	signal write_pointer : memory_pointer_t; | ||||||
|  | 	signal usage_counter : usage_counter_t; | ||||||
|  | begin | ||||||
|  | 	fifo_proc : process(clk, rst) is | ||||||
|  | 		procedure increment_pointer(signal pointer : inout memory_pointer_t) is | ||||||
|  | 		begin | ||||||
|  | 			if pointer = pointer'subtype'high then | ||||||
|  | 				pointer <= 0; | ||||||
|  | 			else | ||||||
|  | 				pointer <= pointer + 1; | ||||||
|  | 			end if; | ||||||
|  | 		end procedure increment_pointer; | ||||||
|  | 
 | ||||||
|  | 		variable pushed : boolean; | ||||||
|  | 		variable popped : boolean; | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 			read_pointer  <= 0; | ||||||
|  | 			write_pointer <= 0; | ||||||
|  | 			usage_counter <= 0; | ||||||
|  | 
 | ||||||
|  | 		elsif rising_edge(clk) then | ||||||
|  | 			pushed := false; | ||||||
|  | 			popped := false; | ||||||
|  | 
 | ||||||
|  | 			if push and not full then | ||||||
|  | 				memory(write_pointer) <= data_in; | ||||||
|  | 				increment_pointer(write_pointer); | ||||||
|  | 				usage_counter         <= usage_counter + 1; | ||||||
|  | 				pushed                := true; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			if pop and not empty then | ||||||
|  | 				increment_pointer(read_pointer); | ||||||
|  | 				usage_counter <= usage_counter - 1; | ||||||
|  | 				popped        := true; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			if pushed and popped then | ||||||
|  | 				usage_counter <= usage_counter; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			if clear then | ||||||
|  | 				read_pointer  <= 0; | ||||||
|  | 				write_pointer <= 0; | ||||||
|  | 				usage_counter <= 0; | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process fifo_proc; | ||||||
|  | 
 | ||||||
|  | 	data_out <= memory(read_pointer); | ||||||
|  | 	empty    <= '1' when usage_counter = 0 else '0'; | ||||||
|  | 	full     <= '1' when usage_counter = DEPTH else '0'; | ||||||
|  | 
 | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										64
									
								
								trashernet/timer.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								trashernet/timer.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- timer.vhd : Simple Timer | ||||||
|  | --   Implements a very simple integer timer | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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; | ||||||
|  | 
 | ||||||
|  | entity timer is | ||||||
|  | 	generic( | ||||||
|  | 		F_TICK    : integer;            -- Frequency of `tick` (or `clk` if tick is tied to '1') | ||||||
|  | 		DURATION  : time;               -- Duration of the timer | ||||||
|  | 		AUTOSTART : boolean := false    -- Automatically start the timer after reset | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		clk         : in  std_logic;    -- Global clock | ||||||
|  | 		rst         : in  std_logic;    -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		tick        : in  std_logic;    -- Timer enable | ||||||
|  | 		start       : in  std_logic;    -- (Re-)start timer | ||||||
|  | 
 | ||||||
|  | 		expired     : out std_logic;    -- Timer is expired | ||||||
|  | 		expired_stb : out std_logic     -- Strobe when timer reaches zero | ||||||
|  | 	); | ||||||
|  | end entity timer; | ||||||
|  | 
 | ||||||
|  | architecture rtl of timer is | ||||||
|  | 	constant PERIOD : time    := 1 sec / real(F_TICK); | ||||||
|  | 	constant TICKS  : integer := integer(DURATION / PERIOD); | ||||||
|  | 
 | ||||||
|  | 	signal counter : integer range 0 to TICKS; | ||||||
|  | begin | ||||||
|  | 	timer_p : process(clk, rst) is | ||||||
|  | 	begin | ||||||
|  | 		if rst then | ||||||
|  | 			counter     <= TICKS when AUTOSTART else 0; | ||||||
|  | 			expired_stb <= '0'; | ||||||
|  | 
 | ||||||
|  | 		elsif rising_edge(clk) then | ||||||
|  | 			expired_stb <= '0'; | ||||||
|  | 			if tick then | ||||||
|  | 				if counter = 1 then | ||||||
|  | 					expired_stb <= '1'; | ||||||
|  | 				end if; | ||||||
|  | 				if counter /= 0 then | ||||||
|  | 					counter <= counter - 1; | ||||||
|  | 				end if; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			if start then | ||||||
|  | 				counter <= TICKS; | ||||||
|  | 			end if; | ||||||
|  | 		end if; | ||||||
|  | 	end process timer_p; | ||||||
|  | 
 | ||||||
|  | 	expired <= not start when counter = 0 else '0'; | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										317
									
								
								trashernet/trashernet_arp.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								trashernet/trashernet_arp.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,317 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- trashernet_arp.vhd : Address Resolution Protocol | ||||||
|  | --   Implements simple single-entry cache ARP. | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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_arp is | ||||||
|  | 	generic( | ||||||
|  | 		SYSTICK_FREQ : integer | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk             : in  std_logic; -- Global clock | ||||||
|  | 		rst             : in  std_logic; -- Asynchronous reset | ||||||
|  | 		systick         : in  std_logic; -- Global time reference for slow events (here: timeout) | ||||||
|  | 
 | ||||||
|  | 		-- Configuration | ||||||
|  | 		mac_config      : in  configuration_t; -- Trashernet MAC configuration | ||||||
|  | 		ip_config       : in  ipv4_configuration_t; -- Trashernet IP configuration | ||||||
|  | 
 | ||||||
|  | 		-- ARP application interface | ||||||
|  | 		arp_out         : out arp_out_t; -- ARP IF (out from ARP) | ||||||
|  | 		arp_in          : in  arp_in_t; -- ARP IF (into ARP) | ||||||
|  | 
 | ||||||
|  | 		-- Ethernet II application interface | ||||||
|  | 		ethernet_ii_out : in  ethernet_ii_out_t; -- Ethernet II IF (out from MAC) | ||||||
|  | 		ethernet_ii_in  : out ethernet_ii_in_t -- Ethernet II IF (into MAC) | ||||||
|  | 	); | ||||||
|  | end entity trashernet_arp; | ||||||
|  | 
 | ||||||
|  | architecture rtl of trashernet_arp is | ||||||
|  | 	constant HTYPE        : byte_vector := (x"00", x"01"); | ||||||
|  | 	constant PTYPE        : byte_vector := (x"08", x"00"); | ||||||
|  | 	constant HLEN         : byte        := x"06"; | ||||||
|  | 	constant PLEN         : byte        := x"04"; | ||||||
|  | 	constant OPER_REQUEST : byte_vector := (x"00", x"01"); | ||||||
|  | 	constant OPER_REPLY   : byte_vector := (x"00", x"02"); | ||||||
|  | 
 | ||||||
|  | 	constant ARP_TIMEOUT : time := 1 sec; | ||||||
|  | 
 | ||||||
|  | 	signal arp_tx_reply_rq  : std_logic; -- Request to transmit an ARP reply | ||||||
|  | 	signal arp_tx_reply_tha : mac_addr_t; -- Target MAC to use in reply (SHA in received packet) | ||||||
|  | 	signal arp_tx_reply_tpa : ip_addr_t; -- Target IP to use in reply (SPA in received packet) | ||||||
|  | 	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_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; | ||||||
|  | 
 | ||||||
|  | 		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 | ||||||
|  | 
 | ||||||
|  | 		arp_resolver_main : process(rst, clk) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				state                <= IDLE; | ||||||
|  | 				replied_ip           <= (others => x"00"); -- 0.0.0.0 | ||||||
|  | 				arp_out.arp_ok_stb   <= '0'; | ||||||
|  | 				arp_out.arp_fail_stb <= '0'; | ||||||
|  | 				arp_tx_request_rq    <= '0'; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				arp_out.arp_ok_stb   <= '0'; | ||||||
|  | 				arp_out.arp_fail_stb <= '0'; | ||||||
|  | 
 | ||||||
|  | 				if arp_rx_reply_stb then | ||||||
|  | 					if arp_tx_request_tpa = arp_rx_spa then -- If this reply is actually for our request | ||||||
|  | 						replied_mac <= arp_rx_sha; | ||||||
|  | 						replied_ip  <= arp_rx_spa; | ||||||
|  | 					end if; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				case state is | ||||||
|  | 					when IDLE => | ||||||
|  | 						if arp_in.arp_query_stb then | ||||||
|  | 							if query_mac_found then | ||||||
|  | 								arp_out.arp_ok_stb <= '1'; | ||||||
|  | 							else | ||||||
|  | 								arp_tx_request_rq <= '1'; | ||||||
|  | 								state             <= QUERY_MAC; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when QUERY_MAC => | ||||||
|  | 						if arp_tx_request_ack then | ||||||
|  | 							arp_tx_request_rq <= '0'; | ||||||
|  | 						end if; | ||||||
|  | 						if query_mac_found or query_timeout then | ||||||
|  | 							arp_out.arp_ok_stb   <= query_mac_found; | ||||||
|  | 							arp_out.arp_fail_stb <= not query_mac_found; | ||||||
|  | 							state                <= IDLE; | ||||||
|  | 						end if; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process arp_resolver_main; | ||||||
|  | 		query_mac_found <= '1' when (arp_tx_request_tpa = replied_ip) else '0'; | ||||||
|  | 
 | ||||||
|  | 		arp_out.arp_mac    <= replied_mac; | ||||||
|  | 		arp_tx_request_tpa <= arp_in.arp_ip; | ||||||
|  | 
 | ||||||
|  | 		timeout_timer_inst : entity work.timer | ||||||
|  | 			generic map( | ||||||
|  | 				F_TICK    => SYSTICK_FREQ, | ||||||
|  | 				DURATION  => ARP_TIMEOUT, | ||||||
|  | 				AUTOSTART => false | ||||||
|  | 			) | ||||||
|  | 			port map( | ||||||
|  | 				clk         => clk, | ||||||
|  | 				rst         => rst, | ||||||
|  | 				tick        => systick, | ||||||
|  | 				start       => arp_tx_request_rq, | ||||||
|  | 				expired     => query_timeout, | ||||||
|  | 				expired_stb => open | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 	end block resolver; | ||||||
|  | 
 | ||||||
|  | 	rx : block | ||||||
|  | 		constant BYTECOUNT_HEAD      : integer := 8; -- HTYPE -> OPER | ||||||
|  | 		constant BYTECOUNT_ADDRESSES : integer := 20; -- SHA -> TPA | ||||||
|  | 
 | ||||||
|  | 		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); | ||||||
|  | 
 | ||||||
|  | 		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); | ||||||
|  | 
 | ||||||
|  | 		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; | ||||||
|  | 
 | ||||||
|  | 		signal block_done : std_logic; | ||||||
|  | 
 | ||||||
|  | 		signal header_ok    : std_logic; | ||||||
|  | 		signal we_are_asked : std_logic; | ||||||
|  | 
 | ||||||
|  | 		signal is_request : std_logic; | ||||||
|  | 	begin | ||||||
|  | 
 | ||||||
|  | 		block_done <= '1' when shifted = '1' and bytecount = 0 else '0'; | ||||||
|  | 
 | ||||||
|  | 		header_ok <= '1' when           -- | ||||||
|  | 		             sr_head_htype = HTYPE and -- | ||||||
|  | 		             sr_head_ptype = PTYPE and -- | ||||||
|  | 		             sr_head_hlen = HLEN and sr_head_plen = PLEN -- | ||||||
|  | 		             else | ||||||
|  | 		             '0'; | ||||||
|  | 
 | ||||||
|  | 		we_are_asked <= '1' when        -- | ||||||
|  | 		                sr_addresses_tpa = ip_config.ip_address else | ||||||
|  | 		                '0'; | ||||||
|  | 
 | ||||||
|  | 		rx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				state            <= HEAD; | ||||||
|  | 				shifted          <= '0'; | ||||||
|  | 				bytecount        <= BYTECOUNT_HEAD; | ||||||
|  | 				arp_rx_reply_stb <= '0'; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				arp_rx_reply_stb <= '0'; | ||||||
|  | 
 | ||||||
|  | 				if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then | ||||||
|  | 					state     <= HEAD;  -- Safe return from any state, may be overridden below | ||||||
|  | 					bytecount <= BYTECOUNT_HEAD; | ||||||
|  | 				end if; | ||||||
|  | 				shifted <= ethernet_ii_out.rx_data_valid; | ||||||
|  | 				if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then | ||||||
|  | 					sr        <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data; | ||||||
|  | 					bytecount <= bytecount - 1; | ||||||
|  | 				end if; | ||||||
|  | 				case state is | ||||||
|  | 					when HEAD => | ||||||
|  | 						if block_done then | ||||||
|  | 							if header_ok then | ||||||
|  | 								bytecount  <= BYTECOUNT_ADDRESSES; | ||||||
|  | 								state      <= ADDRESSES; | ||||||
|  | 								is_request <= '1' when (sr_head_oper = OPER_REQUEST) else '0'; | ||||||
|  | 							else | ||||||
|  | 								state <= IGNORE; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when ADDRESSES => | ||||||
|  | 						if block_done then | ||||||
|  | 							if (we_are_asked) then | ||||||
|  | 								state <= WAITCRC; | ||||||
|  | 							else | ||||||
|  | 								state <= IGNORE; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when WAITCRC => | ||||||
|  | 						if ethernet_ii_out.rx_crc_ok then | ||||||
|  | 							if is_request then | ||||||
|  | 								state <= SENDREPLY; | ||||||
|  | 							else | ||||||
|  | 								arp_rx_reply_stb <= '1'; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when SENDREPLY => | ||||||
|  | 						if arp_tx_reply_ack then | ||||||
|  | 							bytecount <= BYTECOUNT_HEAD; | ||||||
|  | 							state     <= HEAD; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when IGNORE =>      -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err) | ||||||
|  | 						null; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process rx_fsm; | ||||||
|  | 		arp_tx_reply_rq  <= '1' when state = SENDREPLY else '0'; | ||||||
|  | 		arp_tx_reply_tha <= sr_addresses_sha; | ||||||
|  | 		arp_tx_reply_tpa <= sr_addresses_spa; | ||||||
|  | 		arp_rx_sha       <= sr_addresses_sha; | ||||||
|  | 		arp_rx_spa       <= sr_addresses_spa; | ||||||
|  | 	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; | ||||||
|  | 
 | ||||||
|  | 		type   state_t is (IDLE, TRANSMIT); | ||||||
|  | 		signal state   : state_t; | ||||||
|  | 	begin | ||||||
|  | 
 | ||||||
|  | 		arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header | ||||||
|  | 		                     mac_config.mac_address & ip_config.ip_address & -- Sender | ||||||
|  | 		                     arp_tx_reply_tha & arp_tx_reply_tpa; -- Target | ||||||
|  | 
 | ||||||
|  | 		arp_request_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REQUEST & -- Header | ||||||
|  | 		                       mac_config.mac_address & ip_config.ip_address & -- Sender | ||||||
|  | 		                       mac_addr_t'(x"00", x"00", x"00", x"00", x"00", x"00") & arp_tx_request_tpa; -- Target | ||||||
|  | 
 | ||||||
|  | 		tx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				arp_tx_reply_ack   <= '0'; | ||||||
|  | 				arp_tx_request_ack <= '0'; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				arp_tx_reply_ack   <= '0'; | ||||||
|  | 				arp_tx_request_ack <= '0'; | ||||||
|  | 
 | ||||||
|  | 				if (ethernet_ii_out.tx_data_ack = '1') and (bytecount > 0) then | ||||||
|  | 					sr        <= sr(sr'low + 1 to sr'high) & x"00"; | ||||||
|  | 					bytecount <= bytecount - 1; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				case state is | ||||||
|  | 					when IDLE => | ||||||
|  | 						if arp_tx_reply_rq then | ||||||
|  | 							state                         <= TRANSMIT; | ||||||
|  | 							arp_tx_reply_ack              <= '1'; | ||||||
|  | 							sr                            <= arp_reply_preload; | ||||||
|  | 							ethernet_ii_in.tx_mac_address <= arp_tx_reply_tha; | ||||||
|  | 						elsif arp_tx_request_rq then | ||||||
|  | 							state                         <= TRANSMIT; | ||||||
|  | 							arp_tx_request_ack            <= '1'; | ||||||
|  | 							sr                            <= arp_request_preload; | ||||||
|  | 							ethernet_ii_in.tx_mac_address <= MAC_ADDR_BROADCAST; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 						bytecount <= BYTECOUNT_MAX; | ||||||
|  | 
 | ||||||
|  | 					when TRANSMIT => | ||||||
|  | 						if bytecount = 0 then | ||||||
|  | 							state <= IDLE; | ||||||
|  | 						end if; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process tx_fsm; | ||||||
|  | 
 | ||||||
|  | 		ethernet_ii_in.tx_en   <= '1' when state = TRANSMIT else '0'; | ||||||
|  | 		ethernet_ii_in.tx_data <= sr(0); | ||||||
|  | 
 | ||||||
|  | 	end block tx; | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										179
									
								
								trashernet/trashernet_eth.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								trashernet/trashernet_eth.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- trashernet_eth.vhd : Ethernet OSI Layer 2, Data Link, ETH I/II Ethertype | ||||||
|  | --   Implements arbitration of different ethertypes. | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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_eth is | ||||||
|  | 	generic( | ||||||
|  | 		ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ETHERNET_II_PROTOCOLS_NONE | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk             : in  std_logic; -- Global clock | ||||||
|  | 		rst             : in  std_logic; -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		-- MAC application interface | ||||||
|  | 		mac_out         : in  mac_out_t; -- MAC application IF (out of MAC) | ||||||
|  | 		mac_in          : out mac_in_t; -- MAC application IF (into MAC) | ||||||
|  | 
 | ||||||
|  | 		-- Configuration | ||||||
|  | 		config          : in  configuration_t; -- Global Trashernet configuration | ||||||
|  | 
 | ||||||
|  | 		-- Ethernet 802.3 application interface | ||||||
|  | 		ethernet_i_out  : out ethernet_i_out_t; -- Ethernet 802.3 IF (out from MAC) | ||||||
|  | 		ethernet_i_in   : in  ethernet_i_in_t                                    := ETHERNET_I_IN_UNUSED; -- Ethernet 802.3 IF (out from MAC) | ||||||
|  | 
 | ||||||
|  | 		-- Ethernet II application interface | ||||||
|  | 		ethernet_ii_out : out ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); -- Ethernet II IF (out from MAC) | ||||||
|  | 		ethernet_ii_in  : in  ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range) := (others => ETHERNET_II_IN_UNUSED) -- Ethernet II IF (into MAC) | ||||||
|  | 	); | ||||||
|  | end entity trashernet_eth; | ||||||
|  | 
 | ||||||
|  | architecture rtl of trashernet_eth is | ||||||
|  | 	signal tx_data_ack_ii : std_logic_vector(ethernet_ii_out'range); | ||||||
|  | 	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; | ||||||
|  | 
 | ||||||
|  | 	begin | ||||||
|  | 		mac_destination_matches <= '1' when -- | ||||||
|  | 		                           ((mac_out.rx_header.mac_destination = MAC_ADDR_BROADCAST) or -- | ||||||
|  | 		                            (mac_out.rx_header.mac_destination = config.mac_address)) else | ||||||
|  | 		                           '0'; | ||||||
|  | 
 | ||||||
|  | 		mux : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				sel                       <= SEL_ETH_NONE; | ||||||
|  | 				rx_mac_header_rcv_delayed <= '0'; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				rx_mac_header_rcv_delayed <= mac_out.rx_mac_header_rcv; | ||||||
|  | 
 | ||||||
|  | 				if mac_out.rx_mac_header_rcv then | ||||||
|  | 					sel <= SEL_ETH_NONE; -- By default, let's assume it's not for us | ||||||
|  | 					if mac_destination_matches then | ||||||
|  | 						if (unsigned(std_logic_vector'(mac_out.rx_header.mac_ethertype(0) & mac_out.rx_header.mac_ethertype(1))) < 1500) then -- Ethernet 802.3 Frame | ||||||
|  | 							sel <= SEL_ETH_I; | ||||||
|  | 
 | ||||||
|  | 						else            -- Ethernet II Frame | ||||||
|  | 							for i in ETHERNET_II_PROTOCOLS'range loop | ||||||
|  | 								if (mac_out.rx_header.mac_ethertype = ETHERNET_II_PROTOCOLS(i).ethertype) then | ||||||
|  | 									sel <= i; | ||||||
|  | 								end if; | ||||||
|  | 							end loop; | ||||||
|  | 						end if; | ||||||
|  | 					end if; | ||||||
|  | 				end if; | ||||||
|  | 			end if; | ||||||
|  | 		end process mux; | ||||||
|  | 
 | ||||||
|  | 		-- 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'; | ||||||
|  | 		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)); | ||||||
|  | 
 | ||||||
|  | 		-- 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 | ||||||
|  | 			ethernet_ii_out(i).tx_data_ack <= tx_data_ack_ii(i); | ||||||
|  | 		end generate; | ||||||
|  | 		ethernet_i_out.tx_data_ack <= tx_data_ack_i; | ||||||
|  | 	end block rx; | ||||||
|  | 
 | ||||||
|  | 	tx : block | ||||||
|  | 		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; | ||||||
|  | 	begin | ||||||
|  | 		arb : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				sel <= SEL_ETH_I; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				case state is | ||||||
|  | 					when IDLE => | ||||||
|  | 						if not mac_out.tx_active then | ||||||
|  | 							if (ethernet_i_in.tx_en) then -- ETH I has priority | ||||||
|  | 								sel   <= SEL_ETH_I; | ||||||
|  | 								state <= TXD; | ||||||
|  | 							else | ||||||
|  | 								for i in ethernet_ii_in'range loop | ||||||
|  | 									if ethernet_ii_in(i).tx_en then | ||||||
|  | 										sel   <= i; | ||||||
|  | 										state <= TXD; | ||||||
|  | 										exit; -- Prioritize according to vector | ||||||
|  | 									end if; | ||||||
|  | 								end loop; | ||||||
|  | 							end if; | ||||||
|  | 						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; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process arb; | ||||||
|  | 
 | ||||||
|  | 		mux : process(all) is | ||||||
|  | 		begin | ||||||
|  | 			-- Defaults to avoid latch | ||||||
|  | 			tx_data_ack_i  <= '0'; | ||||||
|  | 			tx_data_ack_ii <= (others => '0'); | ||||||
|  | 
 | ||||||
|  | 			-- Actual MUX | ||||||
|  | 			if (sel = SEL_ETH_I) then | ||||||
|  | 				mac_in.tx_mac_data_en            <= ethernet_i_in.tx_en; | ||||||
|  | 				mac_in.tx_mac_data               <= ethernet_i_in.tx_data; | ||||||
|  | 				tx_data_ack_i                    <= mac_out.tx_mac_data_ack; | ||||||
|  | 				mac_in.tx_header.mac_destination <= ethernet_i_in.tx_mac_address; | ||||||
|  | 				mac_in.tx_header.mac_ethertype   <= byte(ethernet_i_in.tx_length(15 downto 8)) & byte(ethernet_i_in.tx_length(7 downto 0)); | ||||||
|  | 			else | ||||||
|  | 				mac_in.tx_mac_data_en <= ethernet_ii_in(sel).tx_en; | ||||||
|  | 				mac_in.tx_mac_data    <= ethernet_ii_in(sel).tx_data; | ||||||
|  | 				txack : for i in ethernet_ii_out'range loop | ||||||
|  | 					tx_data_ack_ii(i) <= mac_out.tx_mac_data_ack when sel = i else '0'; | ||||||
|  | 				end loop txack; | ||||||
|  | 
 | ||||||
|  | 				mac_in.tx_header.mac_destination <= ethernet_ii_in(sel).tx_mac_address; | ||||||
|  | 				mac_in.tx_header.mac_ethertype   <= ETHERNET_II_PROTOCOLS(sel).ethertype; | ||||||
|  | 			end if; | ||||||
|  | 
 | ||||||
|  | 			mac_in.tx_header.mac_source <= config.mac_address; | ||||||
|  | 		end process mux; | ||||||
|  | 	end block tx; | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										172
									
								
								trashernet/trashernet_icmp.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								trashernet/trashernet_icmp.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,172 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- trashernet_icmp.vhd : Ethernet OSI Layer 3, Network (ICMP) | ||||||
|  | --   Implements ICMP echo replies | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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_icmp is | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk               : in  std_logic; -- Global clock | ||||||
|  | 		rst               : in  std_logic; -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		-- IPv4 application interface | ||||||
|  | 		ipv4_protocol_out : in  ipv4_protocol_out_t; -- IPv4 Protocol IF (out from IP Mux) | ||||||
|  | 		ipv4_protocol_in  : out ipv4_protocol_in_t -- IPv4 Protocol IF (into IP Mux) | ||||||
|  | 	); | ||||||
|  | end entity trashernet_icmp; | ||||||
|  | 
 | ||||||
|  | architecture rtl of trashernet_icmp is | ||||||
|  | 	signal fifo_clear    : std_logic; | ||||||
|  | 	signal fifo_data_in  : std_logic_vector(byte'range); | ||||||
|  | 	signal fifo_push     : std_logic; | ||||||
|  | 	signal fifo_full     : std_logic;   -- TODO: FIFO full error handling | ||||||
|  | 	signal fifo_data_out : std_logic_vector(fifo_data_in'range); | ||||||
|  | 	signal fifo_pop      : std_logic; | ||||||
|  | 	signal fifo_empty    : std_logic; | ||||||
|  | 
 | ||||||
|  | 	signal tx_response : std_logic; | ||||||
|  | 	signal rx_checksum : std_logic_vector(15 downto 0); | ||||||
|  | begin | ||||||
|  | 	fifo_inst : entity work.fifo | ||||||
|  | 		generic map( | ||||||
|  | 			DEPTH => 64 | ||||||
|  | 		) | ||||||
|  | 		port map( | ||||||
|  | 			clk      => clk, | ||||||
|  | 			rst      => rst, | ||||||
|  | 			clear    => fifo_clear, | ||||||
|  | 			data_in  => fifo_data_in, | ||||||
|  | 			push     => fifo_push, | ||||||
|  | 			full     => fifo_full, | ||||||
|  | 			data_out => fifo_data_out, | ||||||
|  | 			pop      => fifo_pop, | ||||||
|  | 			empty    => fifo_empty | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 	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); | ||||||
|  | 
 | ||||||
|  | 		type   state_t is (HEADER, DATA, IGNORE); | ||||||
|  | 		signal state   : state_t; | ||||||
|  | 
 | ||||||
|  | 		signal byte_count : integer range 0 to sr'length; | ||||||
|  | 
 | ||||||
|  | 	begin | ||||||
|  | 		rx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				state      <= HEADER; | ||||||
|  | 				byte_count <= byte_count'subtype'high; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 
 | ||||||
|  | 				if (ipv4_protocol_out.rx_data_valid = '1') and (byte_count /= 0) then | ||||||
|  | 					byte_count <= byte_count - 1; | ||||||
|  | 					if state = HEADER then | ||||||
|  | 						sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data; | ||||||
|  | 					end if; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				if ipv4_protocol_out.rx_header_rcv then | ||||||
|  | 					byte_count <= byte_count'subtype'high; | ||||||
|  | 					state      <= HEADER; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				case state is | ||||||
|  | 					when HEADER => | ||||||
|  | 						if byte_count = 0 then | ||||||
|  | 							if sr_type = x"08" then | ||||||
|  | 								state <= DATA; | ||||||
|  | 							else | ||||||
|  | 								state <= IGNORE; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when DATA => | ||||||
|  | 						null;           -- We just wait here and collect data | ||||||
|  | 
 | ||||||
|  | 					when IGNORE =>      -- Just wait until it's over | ||||||
|  | 						null; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process rx_fsm; | ||||||
|  | 		fifo_clear   <= ipv4_protocol_out.rx_header_rcv; -- TODO: This will break things when we get another Ping too soon | ||||||
|  | 		fifo_push    <= ipv4_protocol_out.rx_data_valid when state = DATA else '0'; | ||||||
|  | 		fifo_data_in <= ipv4_protocol_out.rx_data; | ||||||
|  | 
 | ||||||
|  | 		tx_response <= ipv4_protocol_out.rx_ok_stb when state = DATA else '0'; | ||||||
|  | 		rx_checksum <= sr_checksum(2) & sr_checksum(3); | ||||||
|  | 	end block rx; | ||||||
|  | 
 | ||||||
|  | 	tx : block | ||||||
|  | 		signal checksum      : unsigned(16 downto 0); | ||||||
|  | 		signal checksum_ones : unsigned(15 downto 0); | ||||||
|  | 
 | ||||||
|  | 		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; | ||||||
|  | 	begin | ||||||
|  | 
 | ||||||
|  | 		checksum      <= ('0' & not (unsigned(rx_checksum))) - unsigned'('0' & x"0800"); -- The only change is Type 8 changes to Type 0 | ||||||
|  | 		checksum_ones <= not (checksum(15 downto 0) - checksum(16)); | ||||||
|  | 
 | ||||||
|  | 		tx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				if (ipv4_protocol_out.tx_data_ack = '1') and (byte_count /= 0) then | ||||||
|  | 					sr         <= sr(sr'low + 1 to sr'high) & x"00"; | ||||||
|  | 					byte_count <= byte_count - 1; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				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; | ||||||
|  | 							sr                             <= byte_vector'( | ||||||
|  | 								x"00", | ||||||
|  | 								x"00", | ||||||
|  | 								std_logic_vector(checksum_ones(15 downto 8)), | ||||||
|  | 								std_logic_vector(checksum_ones(7 downto 0)) | ||||||
|  | 							); | ||||||
|  | 							byte_count                     <= sr'subtype'high; | ||||||
|  | 							state                          <= HEADER; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when HEADER => | ||||||
|  | 						if (byte_count = 0) and (ipv4_protocol_out.tx_data_ack = '1') then | ||||||
|  | 							state <= PAYLOAD; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when PAYLOAD => | ||||||
|  | 						if fifo_empty then | ||||||
|  | 							state <= IDLE; | ||||||
|  | 						end if; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process tx_fsm; | ||||||
|  | 
 | ||||||
|  | 		fifo_pop                 <= ipv4_protocol_out.tx_data_ack when state = PAYLOAD else '0'; | ||||||
|  | 		ipv4_protocol_in.tx_en   <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; | ||||||
|  | 		ipv4_protocol_in.tx_data <= sr(sr'low) when state = HEADER else fifo_data_out; | ||||||
|  | 	end block tx; | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										263
									
								
								trashernet/trashernet_ipv4.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								trashernet/trashernet_ipv4.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,263 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- trashernet_ipv4.vhd : Ethernet OSI Layer 3, Network (IPv4) | ||||||
|  | --   Implements packet handling and IP-Layer en-/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_ipv4 is | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk             : in  std_logic; -- Global clock | ||||||
|  | 		rst             : in  std_logic; -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		-- Configuration | ||||||
|  | 		ipv4_config     : in  ipv4_configuration_t; -- Trashernet IP configuration | ||||||
|  | 
 | ||||||
|  | 		-- ARP application interface | ||||||
|  | 		arp_out         : in  arp_out_t; -- ARP IF (out from ARP) | ||||||
|  | 		arp_in          : out arp_in_t; -- ARP IF (into ARP) | ||||||
|  | 
 | ||||||
|  | 		-- Ethernet II application interface | ||||||
|  | 		ethernet_ii_out : in  ethernet_ii_out_t; -- Ethernet II IF (out from MAC) | ||||||
|  | 		ethernet_ii_in  : out ethernet_ii_in_t; -- Ethernet II IF (into MAC) | ||||||
|  | 
 | ||||||
|  | 		-- IPv4 application interface | ||||||
|  | 		ipv4_out        : out ipv4_out_t; -- IPv4 IF (out from IP) | ||||||
|  | 		ipv4_in         : in  ipv4_in_t -- IPv4 IF (into IP) | ||||||
|  | 	); | ||||||
|  | end entity trashernet_ipv4; | ||||||
|  | 
 | ||||||
|  | architecture rtl of trashernet_ipv4 is | ||||||
|  | begin | ||||||
|  | 	rx : block | ||||||
|  | 		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); | ||||||
|  | 		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); | ||||||
|  | 
 | ||||||
|  | 		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; | ||||||
|  | 	begin | ||||||
|  | 		sr_fragment_offset <= sr(6)(4 downto 0) & sr(7); | ||||||
|  | 
 | ||||||
|  | 		block_done <= '1' when bytecount = 0 else '0'; | ||||||
|  | 		header_ok  <= '1' when          -- | ||||||
|  | 		              (std_logic_vector'(sr_flags(7) & sr_flags(5)) = "00") and -- Reserved = 0, MF unset | ||||||
|  | 		              (to_integer(unsigned(sr_fragment_offset)) = 0) and -- No fragment offset -> not last fragment | ||||||
|  | 		              (sr_destination_ip = ipv4_config.ip_address) else -- It's addressed to us. TODO: We do not support broadcasts | ||||||
|  | 		              '0'; | ||||||
|  | 		rx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				state                  <= DONE; | ||||||
|  | 				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_data_valid = '1') and (bytecount > 0) then | ||||||
|  | 					if state = HEADER then | ||||||
|  | 						sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data; | ||||||
|  | 					end if; | ||||||
|  | 					bytecount <= bytecount - 1; | ||||||
|  | 				end if; | ||||||
|  | 
 | ||||||
|  | 				case state is | ||||||
|  | 					when HEADER => | ||||||
|  | 						if block_done then | ||||||
|  | 							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; | ||||||
|  | 
 | ||||||
|  | 					when OPT => | ||||||
|  | 						if block_done then | ||||||
|  | 							bytecount <= to_integer(unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3)))) - to_integer(unsigned(sr_ihl)) * 4; | ||||||
|  | 							state     <= PAYLOAD; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when PAYLOAD => | ||||||
|  | 						if block_done then | ||||||
|  | 							state <= DONE; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					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; | ||||||
|  | 
 | ||||||
|  | 	end block rx; | ||||||
|  | 
 | ||||||
|  | 	tx : block | ||||||
|  | 		type   state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD); | ||||||
|  | 		signal state   : state_t; | ||||||
|  | 
 | ||||||
|  | 		signal full_length : ipv4_length; | ||||||
|  | 
 | ||||||
|  | 		signal sr            : byte_vector(0 to 19); | ||||||
|  | 		signal byte_cnt      : integer range 0 to sr'length - 1; | ||||||
|  | 		signal alt_byte      : std_logic; | ||||||
|  | 		signal checksum      : unsigned(20 downto 0); -- 20 Header fields -> 19 chances for carry -> 5 additional bits | ||||||
|  | 		signal checksum_ones : unsigned(15 downto 0); | ||||||
|  | 
 | ||||||
|  | 		constant TTL : byte := x"40"; | ||||||
|  | 		impure function get_header(checksum : std_logic_vector(15 downto 0)) return byte_vector is | ||||||
|  | 			variable ret : byte_vector(sr'range); | ||||||
|  | 		begin | ||||||
|  | 			ret :=                      -- | ||||||
|  | 			       byte_vector'( | ||||||
|  | 				       x"45", x"00", byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), -- Ver/IHL, DSCP/ECN, Len | ||||||
|  | 				       x"00", x"00", x"00", x"00" -- Identification, Flags/FragOffset | ||||||
|  | 			       ) & TTL & ipv4_in.tx_protocol & checksum(15 downto 8) & checksum(7 downto 0) & -- TTL, Protocol, Header Checksum | ||||||
|  | 			       ipv4_config.ip_address & -- Source IP | ||||||
|  | 			       ipv4_in.tx_ip_address -- Destination IP | ||||||
|  | 			     ; | ||||||
|  | 			return ret; | ||||||
|  | 		end function get_header; | ||||||
|  | 	begin | ||||||
|  | 		full_length   <= sr'length + ipv4_in.tx_length; | ||||||
|  | 		checksum_ones <= checksum(15 downto 0) + checksum(checksum'high downto 16); | ||||||
|  | 
 | ||||||
|  | 		tx_fsm : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				state                <= IDLE; | ||||||
|  | 				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 | ||||||
|  | 						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 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 => | ||||||
|  | 						if arp_out.arp_ok_stb then | ||||||
|  | 							ethernet_ii_in.tx_mac_address <= arp_out.arp_mac; | ||||||
|  | 
 | ||||||
|  | 							byte_cnt <= sr'length - 1; | ||||||
|  | 							sr       <= get_header(x"0000"); | ||||||
|  | 							alt_byte <= '1'; | ||||||
|  | 							checksum <= (others => '0'); | ||||||
|  | 							state    <= CALC_CKS; | ||||||
|  | 						end if; | ||||||
|  | 						if arp_out.arp_fail_stb then | ||||||
|  | 							ipv4_out.tx_err_stb <= '1'; | ||||||
|  | 							-- Then we lock up in this state until tx_en is disabled | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when CALC_CKS => | ||||||
|  | 						alt_byte <= not alt_byte; | ||||||
|  | 
 | ||||||
|  | 						if alt_byte then | ||||||
|  | 							checksum <= checksum + unsigned(std_logic_vector'(sr(sr'low) & sr(sr'low + 1))); | ||||||
|  | 						else | ||||||
|  | 							if byte_cnt = 0 then | ||||||
|  | 								state    <= HEADER; | ||||||
|  | 								byte_cnt <= sr'length - 1; | ||||||
|  | 								sr       <= get_header(not std_logic_vector(checksum_ones)); | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when HEADER => | ||||||
|  | 						if byte_cnt = 0 then | ||||||
|  | 							state <= PAYLOAD; | ||||||
|  | 						end if; | ||||||
|  | 
 | ||||||
|  | 					when PAYLOAD => | ||||||
|  | 						if ethernet_ii_out.tx_data_ack then | ||||||
|  | 							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'; | ||||||
|  | 							end if; | ||||||
|  | 						end if; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process tx_fsm; | ||||||
|  | 
 | ||||||
|  | 		ethernet_ii_in.tx_data <= sr(sr'low); | ||||||
|  | 		ethernet_ii_in.tx_en   <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; | ||||||
|  | 	end block tx; | ||||||
|  | 
 | ||||||
|  | end architecture rtl; | ||||||
							
								
								
									
										127
									
								
								trashernet/trashernet_ipv4prot.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								trashernet/trashernet_ipv4prot.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               TRASHERNET - A Trashy Ethernet Stack for FPGAs               -- | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- trashernet_eth.vhd : Ethernet OSI Layer 3, Network, Protocol Muxing | ||||||
|  | --   Implements arbitration of different IP protocols. | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | -- 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_ipv4prot is | ||||||
|  | 	generic( | ||||||
|  | 		IPV4_PROTOCOLS : ipv4_protocol_vector | ||||||
|  | 	); | ||||||
|  | 	port( | ||||||
|  | 		-- Global | ||||||
|  | 		clk               : in  std_logic; -- Global clock | ||||||
|  | 		rst               : in  std_logic; -- Asynchronous reset | ||||||
|  | 
 | ||||||
|  | 		-- IPv4 application interface | ||||||
|  | 		ipv4_out          : in  ipv4_out_t; -- IPv4 IF (out from IP) | ||||||
|  | 		ipv4_in           : out ipv4_in_t; -- IPv4 IF (into IP) | ||||||
|  | 
 | ||||||
|  | 		-- IPv4 protocol interface | ||||||
|  | 		ipv4_protocol_out : out ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); -- IPv4 Protocol IF (out from IP Mux) | ||||||
|  | 		ipv4_protocol_in  : in  ipv4_protocol_in_vector(IPV4_PROTOCOLS'range) -- IPv4 Protocol IF (into IP Mux) | ||||||
|  | 	); | ||||||
|  | end entity trashernet_ipv4prot; | ||||||
|  | 
 | ||||||
|  | architecture rtl of trashernet_ipv4prot is | ||||||
|  | 	constant SEL_PROTOCOL_NONE : integer := ipv4_protocol_out'low - 1; | ||||||
|  | 
 | ||||||
|  | 	signal rx_sel : integer range SEL_PROTOCOL_NONE to ipv4_protocol_out'high; | ||||||
|  | 	signal tx_sel : integer range ipv4_protocol_in'low to ipv4_protocol_in'high; | ||||||
|  | 
 | ||||||
|  | begin | ||||||
|  | 	rx : block | ||||||
|  | 		signal rx_header_rcv_delayed : std_logic; | ||||||
|  | 
 | ||||||
|  | 	begin | ||||||
|  | 		arb : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				rx_sel                <= SEL_PROTOCOL_NONE; | ||||||
|  | 				rx_header_rcv_delayed <= '0'; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				rx_header_rcv_delayed <= ipv4_out.rx_header_rcv; | ||||||
|  | 
 | ||||||
|  | 				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 | ||||||
|  | 							rx_sel <= i; | ||||||
|  | 						end if; | ||||||
|  | 					end loop; | ||||||
|  | 				end if; | ||||||
|  | 			end if; | ||||||
|  | 		end process arb; | ||||||
|  | 
 | ||||||
|  | 		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_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'; | ||||||
|  | 			ipv4_protocol_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0'; | ||||||
|  | 		end generate mux; | ||||||
|  | 
 | ||||||
|  | 		tx_mux : for i in ipv4_protocol_out'range generate | ||||||
|  | 			ipv4_protocol_out(i).tx_data_ack <= ipv4_out.tx_data_ack when tx_sel = i else '0'; | ||||||
|  | 			ipv4_protocol_out(i).tx_err_stb  <= ipv4_out.tx_err_stb when tx_sel = i else '0'; | ||||||
|  | 			ipv4_protocol_out(i).tx_ok_stb   <= ipv4_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 <= ipv4_protocol_in'left; | ||||||
|  | 
 | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				case state is | ||||||
|  | 					when IDLE => | ||||||
|  | 						for i in ipv4_protocol_in'range loop | ||||||
|  | 							if ipv4_protocol_in(i).tx_en then | ||||||
|  | 								tx_sel <= i; | ||||||
|  | 								state  <= TXD; | ||||||
|  | 								exit;   -- Prioritize according to vector | ||||||
|  | 							end if; | ||||||
|  | 						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; | ||||||
|  | 				end case; | ||||||
|  | 			end if; | ||||||
|  | 		end process arb; | ||||||
|  | 
 | ||||||
|  | 		demux : block | ||||||
|  | 		begin | ||||||
|  | 			ipv4_in.tx_data       <= ipv4_protocol_in(tx_sel).tx_data; | ||||||
|  | 			ipv4_in.tx_en         <= ipv4_protocol_in(tx_sel).tx_en; | ||||||
|  | 			ipv4_in.tx_ip_address <= ipv4_protocol_in(tx_sel).tx_ip_address; | ||||||
|  | 			ipv4_in.tx_length     <= ipv4_protocol_in(tx_sel).tx_length; | ||||||
|  | 			ipv4_in.tx_protocol   <= IPV4_PROTOCOLS(tx_sel); | ||||||
|  | 		end block demux; | ||||||
|  | 	end block tx; | ||||||
|  | end architecture rtl; | ||||||
| @ -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 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; | 		constant CRC_LENGTH     : integer := 4; | ||||||
| 		signal state            : state_t; | 		signal   state          : state_t; | ||||||
| 		signal sr_head          : byte_vector(0 to HEAD_LENGTH - 1); | 		signal   sr_head        : byte_vector(0 to HEAD_LENGTH - 1); | ||||||
| 		constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_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); | 		signal sr_payload : byte_vector(0 to 4); | ||||||
| 
 | 
 | ||||||
| @ -79,6 +79,7 @@ begin | |||||||
| 				mac_out.rx_mac_crc_ok     <= '0'; | 				mac_out.rx_mac_crc_ok     <= '0'; | ||||||
| 				mac_out.rx_mac_header_rcv <= '0'; | 				mac_out.rx_mac_header_rcv <= '0'; | ||||||
| 				crc_clear                 <= '0'; | 				crc_clear                 <= '0'; | ||||||
|  | 				state                     <= HEAD; | ||||||
| 
 | 
 | ||||||
| 			elsif rising_edge(clk) then | 			elsif rising_edge(clk) then | ||||||
| 				mac_out.rx_mac_valid      <= '0'; | 				mac_out.rx_mac_valid      <= '0'; | ||||||
| @ -101,7 +102,7 @@ begin | |||||||
| 								byte_count <= byte_count + 1; | 								byte_count <= byte_count + 1; | ||||||
| 							end if; | 							end if; | ||||||
| 						end if; | 						end if; | ||||||
| 					when PAYLOAD => | 					when PAYLOAD =>     -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error) | ||||||
| 						if phy_out.rx_data_valid then | 						if phy_out.rx_data_valid then | ||||||
| 							sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data; | 							sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data; | ||||||
| 							if byte_count = CRC_LENGTH then | 							if byte_count = CRC_LENGTH then | ||||||
| @ -135,14 +136,14 @@ begin | |||||||
| 	end block rx; | 	end block rx; | ||||||
| 
 | 
 | ||||||
| 	tx : block | 	tx : block | ||||||
| 		type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC); | 		type   tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC); | ||||||
| 		signal tx_state : tx_state_t; | 		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; | 		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 | 		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       : std_logic_vector(ETH_POLYNOMIAL'range); | ||||||
| 		signal crc_clear : std_logic; | 		signal crc_clear : std_logic; | ||||||
| @ -165,13 +166,18 @@ begin | |||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 		tx_main : process(clk, rst) is | 		tx_main : process(clk, rst) is | ||||||
|  | 			variable more_data : std_logic; | ||||||
| 		begin | 		begin | ||||||
| 			if rst then | 			if rst then | ||||||
| 				mac_out.tx_mac_data_ack <= '0'; | 				mac_out.tx_mac_data_ack <= '0'; | ||||||
|  | 				tx_state                <= IDLE; | ||||||
|  | 				sr_cnt                  <= 0; | ||||||
|  | 				byte_cnt                <= 0; | ||||||
| 
 | 
 | ||||||
| 			elsif rising_edge(clk) then | 			elsif rising_edge(clk) then | ||||||
| 				mac_out.tx_mac_data_ack <= '0'; | 				mac_out.tx_mac_data_ack <= '0'; | ||||||
| 				crc_valid               <= '0'; | 				crc_valid               <= '0'; | ||||||
|  | 				more_data               := more_data and mac_in.tx_mac_data_en; -- Latch a short low-pulse on tx_en | ||||||
| 
 | 
 | ||||||
| 				if phy_out.tx_data_ack then | 				if phy_out.tx_data_ack then | ||||||
| 					sr        <= sr(sr'low + 1 to sr'high) & x"00"; | 					sr        <= sr(sr'low + 1 to sr'high) & x"00"; | ||||||
| @ -186,6 +192,7 @@ begin | |||||||
| 				case tx_state is | 				case tx_state is | ||||||
| 					when IDLE =>        -- Idle + prepare sync header | 					when IDLE =>        -- Idle + prepare sync header | ||||||
| 						if not phy_out.tx_active and mac_in.tx_mac_data_en then | 						if not phy_out.tx_active and mac_in.tx_mac_data_en then | ||||||
|  | 							more_data  := '1'; | ||||||
| 							sr(0 to 7) <= byte_vector'(0 to 6 => x"55", 7 => x"D5"); | 							sr(0 to 7) <= byte_vector'(0 to 6 => x"55", 7 => x"D5"); | ||||||
| 							sr_cnt     <= 7; | 							sr_cnt     <= 7; | ||||||
| 							tx_state   <= HEADER; | 							tx_state   <= HEADER; | ||||||
| @ -202,7 +209,7 @@ begin | |||||||
| 
 | 
 | ||||||
| 					when DATA =>        -- Wait for MAC header + transmit payload until tx_mac_data_en goes away | 					when DATA =>        -- Wait for MAC header + transmit payload until tx_mac_data_en goes away | ||||||
| 						if (phy_out.tx_data_ack = '1') and (sr_cnt = 0) then -- MAC Header TX complete | 						if (phy_out.tx_data_ack = '1') and (sr_cnt = 0) then -- MAC Header TX complete | ||||||
| 							if mac_in.tx_mac_data_en then | 							if more_data then | ||||||
| 								sr(0)                   <= mac_in.tx_mac_data; | 								sr(0)                   <= mac_in.tx_mac_data; | ||||||
| 								mac_out.tx_mac_data_ack <= '1'; | 								mac_out.tx_mac_data_ack <= '1'; | ||||||
| 							else | 							else | ||||||
|  | |||||||
| @ -39,8 +39,31 @@ end entity trashernet_phy; | |||||||
| architecture rtl of trashernet_phy is | architecture rtl of trashernet_phy is | ||||||
| 	constant F_ETH : integer := 10000000; | 	constant F_ETH : integer := 10000000; | ||||||
| 
 | 
 | ||||||
|  | 	signal tick_ms : std_logic;         -- 1 ms tick (for NLP) | ||||||
| begin | 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; | ||||||
|  | 	begin | ||||||
|  | 		mstick : process(clk, rst) is | ||||||
|  | 		begin | ||||||
|  | 			if rst then | ||||||
|  | 				tick_ms_count <= TICK_MS_CNT_MAX; | ||||||
|  | 				tick_ms       <= '0'; | ||||||
|  | 			elsif rising_edge(clk) then | ||||||
|  | 				tick_ms <= '0'; | ||||||
|  | 				if tick_ms_count = 0 then | ||||||
|  | 					tick_ms       <= '1'; | ||||||
|  | 					tick_ms_count <= TICK_MS_CNT_MAX; | ||||||
|  | 				else | ||||||
|  | 					tick_ms_count <= tick_ms_count - 1; | ||||||
|  | 				end if; | ||||||
|  | 			end if; | ||||||
|  | 		end process mstick; | ||||||
|  | 
 | ||||||
|  | 	end block common; | ||||||
|  | 
 | ||||||
| 	receiver : block | 	receiver : block | ||||||
| 		-- Signal conditioning | 		-- Signal conditioning | ||||||
| 		signal rx             : std_logic; | 		signal rx             : std_logic; | ||||||
| @ -56,8 +79,8 @@ begin | |||||||
| 		signal bit_cnt : integer range 0 to 7; | 		signal bit_cnt : integer range 0 to 7; | ||||||
| 
 | 
 | ||||||
| 		-- NLP supervision | 		-- NLP supervision | ||||||
| 		constant NLP_TIMEOUT_CNT_MAX : integer := integer(round(real(F_CLK) * 16.0 * 10.0**(-3))); -- Every 16 ms | 		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 | 	begin | ||||||
| 		-- Synchronize RX input | 		-- Synchronize RX input | ||||||
| 		synchronizer_rxp_inst : entity work.synchronizer | 		synchronizer_rxp_inst : entity work.synchronizer | ||||||
| @ -88,24 +111,26 @@ begin | |||||||
| 
 | 
 | ||||||
| 		demanchestizer : block | 		demanchestizer : block | ||||||
| 			-- Transition detector | 			-- Transition detector | ||||||
| 			constant BIT_LENGTH_LONG      : integer := F_CLK / F_ETH - 1; | 			constant BIT_LENGTH_LONG       : integer := F_CLK / F_ETH - 1; | ||||||
| 			constant BIT_LENGTH_SHORT     : integer := F_CLK / (F_ETH * 2) - 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_TOLERANCE  : integer := (F_CLK / F_ETH) / 5; | ||||||
| 			constant BIT_LENGTH_TIMEOUT   : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1; | 			constant BIT_LENGTH_TIMEOUT    : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1; | ||||||
| 			signal sample_count           : integer range 0 to BIT_LENGTH_TIMEOUT; | 			signal   sample_count          : integer range 0 to BIT_LENGTH_TIMEOUT; | ||||||
| 			type transition_duration_t is (SHORT, LONG); | 			type     transition_duration_t is (SHORT, LONG); | ||||||
| 			signal transition_duration    : transition_duration_t; | 			signal   transition_duration   : transition_duration_t; | ||||||
| 			signal transition_stb         : std_logic; | 			signal   transition_stb        : std_logic; | ||||||
| 			signal transition_activity    : std_logic; | 			signal   transition_activity   : std_logic; | ||||||
| 
 | 
 | ||||||
| 			-- Transition analysis | 			-- Transition analysis | ||||||
| 			signal last_transition : transition_duration_t; | 			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; | 			signal bit_ev          : bit_ev_t; | ||||||
| 
 | 
 | ||||||
| 			-- Bit recovery | 			-- Bit recovery | ||||||
| 			type demanchestization_state_t is (SYNC, DATA, ERROR); | 			type     demanchestization_state_t          is (SYNC, DATA, ERROR); | ||||||
| 			signal demanchestization_state : demanchestization_state_t; | 			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; | ||||||
| 		begin | 		begin | ||||||
| 			-- Detects spacing of transitions | 			-- Detects spacing of transitions | ||||||
| 			transition_detector : process(clk, rst) is | 			transition_detector : process(clk, rst) is | ||||||
| @ -177,21 +202,30 @@ begin | |||||||
| 			bit_recovery : process(clk, rst) is | 			bit_recovery : process(clk, rst) is | ||||||
| 			begin | 			begin | ||||||
| 				if rst then | 				if rst then | ||||||
| 					demanchestization_state <= SYNC; | 					demanchestization_state        <= SYNC; | ||||||
| 					bit_stb                 <= '0'; | 					bit_stb                        <= '0'; | ||||||
| 					phy_out.rx_active       <= '0'; | 					phy_out.rx_active              <= '0'; | ||||||
| 					phy_out.rx_error        <= '0'; | 					phy_out.rx_error               <= '0'; | ||||||
|  | 					demanchestization_min_sync_cnt <= DEMANCHESTIZATION_MIN_SYNC_CNT_MAX; | ||||||
| 				elsif rising_edge(clk) then | 				elsif rising_edge(clk) then | ||||||
| 					bit_stb          <= '0'; | 					bit_stb          <= '0'; | ||||||
| 					phy_out.rx_error <= '0'; | 					phy_out.rx_error <= '0'; | ||||||
| 
 | 
 | ||||||
|  | 					if (bit_ev = TOGGLE) and (demanchestization_min_sync_cnt /= 0) then | ||||||
|  | 						demanchestization_min_sync_cnt <= demanchestization_min_sync_cnt - 1; | ||||||
|  | 					elsif (bit_ev = KEEP) or (bit_ev = ERROR) then | ||||||
|  | 						demanchestization_min_sync_cnt <= DEMANCHESTIZATION_MIN_SYNC_CNT_MAX; | ||||||
|  | 					end if; | ||||||
|  | 
 | ||||||
| 					if (bit_ev /= NONE) then | 					if (bit_ev /= NONE) then | ||||||
| 						case demanchestization_state is | 						case demanchestization_state is | ||||||
| 							when SYNC => | 							when SYNC => | ||||||
| 								if (bit_ev = KEEP) then | 								if (bit_ev = KEEP) then | ||||||
| 									bit_value               <= '1'; | 									if demanchestization_min_sync_cnt = 0 then | ||||||
| 									demanchestization_state <= DATA; | 										bit_value               <= '1'; | ||||||
| 									phy_out.rx_active       <= '1'; | 										demanchestization_state <= DATA; | ||||||
|  | 										phy_out.rx_active       <= '1'; | ||||||
|  | 									end if; | ||||||
| 								end if; | 								end if; | ||||||
| 							when DATA => -- @suppress: Condition outside of case allows to exit this state | 							when DATA => -- @suppress: Condition outside of case allows to exit this state | ||||||
| 								bit_value <= not bit_value when bit_ev = TOGGLE else bit_value; | 								bit_value <= not bit_value when bit_ev = TOGGLE else bit_value; | ||||||
| @ -202,7 +236,7 @@ begin | |||||||
| 					end if; | 					end if; | ||||||
| 
 | 
 | ||||||
| 					if (bit_ev = ERROR) then | 					if (bit_ev = ERROR) then | ||||||
| 						phy_out.rx_error        <= '1'; | 						phy_out.rx_error        <= '1' when (demanchestization_state = DATA) else '0'; | ||||||
| 						demanchestization_state <= ERROR; | 						demanchestization_state <= ERROR; | ||||||
| 					end if; | 					end if; | ||||||
| 
 | 
 | ||||||
| @ -219,6 +253,7 @@ begin | |||||||
| 		begin | 		begin | ||||||
| 			if rst then | 			if rst then | ||||||
| 				phy_out.rx_data_valid <= '0'; | 				phy_out.rx_data_valid <= '0'; | ||||||
|  | 				phy_out.rx_data       <= (others => '0'); -- Needed for yosys to compile | ||||||
| 
 | 
 | ||||||
| 			elsif rising_edge(clk) then | 			elsif rising_edge(clk) then | ||||||
| 				phy_out.rx_data_valid <= '0'; | 				phy_out.rx_data_valid <= '0'; | ||||||
| @ -248,8 +283,10 @@ begin | |||||||
| 				if rx_edge then         -- Technically, we should use only the rising edge here, but a project called `trashernet` probably won't mind ;) | 				if rx_edge then         -- Technically, we should use only the rising edge here, but a project called `trashernet` probably won't mind ;) | ||||||
| 					nlp_timeout_cnt <= NLP_TIMEOUT_CNT_MAX; | 					nlp_timeout_cnt <= NLP_TIMEOUT_CNT_MAX; | ||||||
| 				else | 				else | ||||||
| 					if (nlp_timeout_cnt /= 0) then | 					if tick_ms then | ||||||
| 						nlp_timeout_cnt <= nlp_timeout_cnt - 1; | 						if nlp_timeout_cnt /= 0 then | ||||||
|  | 							nlp_timeout_cnt <= nlp_timeout_cnt - 1; | ||||||
|  | 						end if; | ||||||
| 					end if; | 					end if; | ||||||
| 				end if; | 				end if; | ||||||
| 			end if; | 			end if; | ||||||
| @ -260,21 +297,25 @@ begin | |||||||
| 	-- ------------------------------------------------------------------------- | 	-- ------------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
| 	transmitter : block | 	transmitter : block | ||||||
| 		constant TX_STB_CNT_MAX : integer := F_CLK / (F_ETH * 2) - 1; | 		constant TX_STB_CNT_IDEAL   : real    := real(F_CLK) / real((F_ETH * 2)); | ||||||
| 		signal tx_stb_cnt       : integer range 0 to TX_STB_CNT_MAX; | 		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; | ||||||
| 
 | 
 | ||||||
| 		type tx_state_t is (IDLE, NLP, TX, IPG); | 		type   tx_state_t is (IDLE, NLP, TX, IPG); | ||||||
| 		signal tx_state  : tx_state_t; | 		signal tx_state   : tx_state_t; | ||||||
| 		signal sr        : std_logic_vector(phy_in.tx_data'range); | 		signal sr         : std_logic_vector(phy_in.tx_data'range); | ||||||
| 		signal bit_stage : std_logic; | 		signal bit_stage  : std_logic; | ||||||
| 
 | 
 | ||||||
| 		constant BIT_CNT_MAX_NLP  : integer := 16000 / 100; -- 16 ms (timebase 100 ns) |  | ||||||
| 		constant BIT_CNT_MAX_IPG  : integer := 96; | 		constant BIT_CNT_MAX_IPG  : integer := 96; | ||||||
| 		constant BIT_CNT_MAX_DATA : integer := sr'length - 1; | 		constant BIT_CNT_MAX_DATA : integer := sr'length - 1; | ||||||
| 		signal bit_cnt            : integer range 0 to maximum(maximum(BIT_CNT_MAX_NLP, BIT_CNT_MAX_DATA), BIT_CNT_MAX_IPG); | 		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; | ||||||
| 
 | 
 | ||||||
| 		type tx_mode_t is (OFF, NLP, ACTIVE); | 		type   tx_mode_t is (OFF, NLP, ACTIVE); | ||||||
| 		signal tx_mode : tx_mode_t; | 		signal tx_mode   : tx_mode_t; | ||||||
| 	begin | 	begin | ||||||
| 		tx_main : process(clk, rst) is | 		tx_main : process(clk, rst) is | ||||||
| 			procedure transmit_byte is | 			procedure transmit_byte is | ||||||
| @ -308,24 +349,31 @@ begin | |||||||
| 				tx_state <= IDLE; | 				tx_state <= IDLE; | ||||||
| 				tx_mode  <= OFF; | 				tx_mode  <= OFF; | ||||||
| 
 | 
 | ||||||
| 				bit_cnt   <= BIT_CNT_MAX_NLP; | 				nlp_cnt   <= NLP_CNT_MAX; | ||||||
| 				bit_stage <= '0'; | 				bit_stage <= '0'; | ||||||
| 			end procedure go_idle; | 			end procedure go_idle; | ||||||
| 
 | 
 | ||||||
| 			variable tx_stb : std_logic; -- Strobe every 50 ns (20 MHz) | 			variable tx_stb : std_logic; -- Strobe every 50 ns (20 MHz) | ||||||
| 
 | 
 | ||||||
|  | 			variable more_data : std_logic; -- tx_data_en low-latched | ||||||
| 		begin | 		begin | ||||||
| 			if rst then | 			if rst then | ||||||
| 				phy_out.tx_data_ack <= '0'; | 				phy_out.tx_data_ack <= '0'; | ||||||
| 				tx_stb_cnt          <= TX_STB_CNT_MAX; | 				tx_stb_cnt          <= TX_STB_CNT_MAX; | ||||||
| 				tx_mode             <= OFF; | 				tx_mode             <= OFF; | ||||||
|  | 				tx_state            <= IDLE; | ||||||
|  | 				bit_cnt             <= 0; | ||||||
| 				go_idle; | 				go_idle; | ||||||
| 
 | 
 | ||||||
| 			elsif rising_edge(clk) then | 			elsif rising_edge(clk) then | ||||||
| 				phy_out.tx_data_ack <= '0'; | 				phy_out.tx_data_ack <= '0'; | ||||||
|  | 				more_data           := more_data and phy_in.tx_data_en; -- Latch a short low-pulse on tx_en | ||||||
| 
 | 
 | ||||||
| 				if tx_stb_cnt = 0 then | 				if tx_stb_cnt = 0 then | ||||||
| 					tx_stb_cnt <= TX_STB_CNT_MAX; | 					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 | 				else | ||||||
| 					tx_stb_cnt <= tx_stb_cnt - 1; | 					tx_stb_cnt <= tx_stb_cnt - 1; | ||||||
| 				end if; | 				end if; | ||||||
| @ -339,15 +387,20 @@ begin | |||||||
| 						bit_cnt <= bit_cnt - 1; | 						bit_cnt <= bit_cnt - 1; | ||||||
| 					end if; | 					end if; | ||||||
| 				end if; | 				end if; | ||||||
| 
 | 				if tick_ms then | ||||||
|  | 					if (nlp_cnt /= 0) then | ||||||
|  | 						nlp_cnt <= nlp_cnt - 1; | ||||||
|  | 					end if; | ||||||
|  | 				end if; | ||||||
| 				case tx_state is | 				case tx_state is | ||||||
| 					when IDLE => | 					when IDLE => | ||||||
| 						if phy_in.tx_data_en then -- New packet to TX | 						if phy_in.tx_data_en then -- New packet to TX | ||||||
| 							transmit_byte; | 							transmit_byte; | ||||||
|  | 							more_data  := '1'; | ||||||
| 							bit_stage  <= '1'; | 							bit_stage  <= '1'; | ||||||
| 							tx_stb_cnt <= TX_STB_CNT_MAX; -- resync | 							tx_stb_cnt <= TX_STB_CNT_MAX; -- resync | ||||||
| 
 | 
 | ||||||
| 						elsif bit_cnt = 0 then -- NLP timeout | 						elsif nlp_cnt = 0 then -- NLP timeout | ||||||
| 							transmit_nlp; | 							transmit_nlp; | ||||||
| 
 | 
 | ||||||
| 							--bit_cnt  <= 1; Let's save some resources here... | 							--bit_cnt  <= 1; Let's save some resources here... | ||||||
| @ -363,7 +416,7 @@ begin | |||||||
| 							if (bit_stage = '1') then | 							if (bit_stage = '1') then | ||||||
| 								sr <= '0' & sr(sr'high downto sr'low + 1); | 								sr <= '0' & sr(sr'high downto sr'low + 1); | ||||||
| 								if bit_cnt = 0 then | 								if bit_cnt = 0 then | ||||||
| 									if phy_in.tx_data_en then | 									if more_data then | ||||||
| 										transmit_byte; | 										transmit_byte; | ||||||
| 									else | 									else | ||||||
| 										transmit_ipg; | 										transmit_ipg; | ||||||
| @ -378,7 +431,7 @@ begin | |||||||
| 				end case; | 				end case; | ||||||
| 			end if; | 			end if; | ||||||
| 		end process tx_main; | 		end process tx_main; | ||||||
| 		phy_out.tx_active <= '1' when tx_state /= IDLE else '0'; | 		phy_out.tx_active <= '1' when (tx_state /= IDLE and tx_state /= NLP) else '0'; | ||||||
| 
 | 
 | ||||||
| 		driver : process(clk, rst) is | 		driver : process(clk, rst) is | ||||||
| 		begin | 		begin | ||||||
| @ -394,8 +447,8 @@ begin | |||||||
| 						tx_p <= '1'; | 						tx_p <= '1'; | ||||||
| 						tx_n <= '0'; | 						tx_n <= '0'; | ||||||
| 					when ACTIVE => | 					when ACTIVE => | ||||||
| 						tx_p <= sr(sr'low) xor bit_stage; | 						tx_p <= sr(sr'low) xnor bit_stage; | ||||||
| 						tx_n <= not (sr(sr'low) xor bit_stage); | 						tx_n <= not (sr(sr'low) xnor bit_stage); | ||||||
| 				end case; | 				end case; | ||||||
| 			end if; | 			end if; | ||||||
| 		end process driver; | 		end process driver; | ||||||
|  | |||||||
| @ -44,12 +44,14 @@ architecture rtl of trashernet_phy_cdc is | |||||||
| 	signal phy_phy_in  : phy_in_t; | 	signal phy_phy_in  : phy_in_t; | ||||||
| 
 | 
 | ||||||
| 	-- Helper signals | 	-- Helper signals | ||||||
| 	signal rx_data_valid_i : std_logic; | 	signal rx_data_i : byte; | ||||||
| 
 | 
 | ||||||
| begin | begin | ||||||
| 	-- ------------------------------------------------------------------------- | 	-- ------------------------------------------------------------------------- | ||||||
| 	-- Drives: PHY clock domain | 	-- Drives: PHY clock domain | ||||||
| 	-- ------------------------------------------------------------------------- | 	-- ------------------------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | 	-- Reset synchronizer for PHY | ||||||
| 	rstsync : process(phy_clk, rst) is | 	rstsync : process(phy_clk, rst) is | ||||||
| 	begin | 	begin | ||||||
| 		if rst then | 		if rst then | ||||||
| @ -59,6 +61,7 @@ begin | |||||||
| 		end if; | 		end if; | ||||||
| 	end process rstsync; | 	end process rstsync; | ||||||
| 
 | 
 | ||||||
|  | 	-- Operate Trashernet in PHY clock domain | ||||||
| 	trashernet_phy_inst : entity work.trashernet_phy | 	trashernet_phy_inst : entity work.trashernet_phy | ||||||
| 		generic map( | 		generic map( | ||||||
| 			F_CLK => F_CLK_PHY | 			F_CLK => F_CLK_PHY | ||||||
| @ -73,6 +76,22 @@ begin | |||||||
| 			tx_n    => tx_n | 			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 | 	synchronizer_txen_inst : entity work.synchronizer | ||||||
| 		generic map( | 		generic map( | ||||||
| 			SIZE => 5 | 			SIZE => 5 | ||||||
| @ -96,18 +115,10 @@ begin | |||||||
| 			a_in  => phy_phy_out.rx_data_valid, | 			a_in  => phy_phy_out.rx_data_valid, | ||||||
| 			b_clk => clk, | 			b_clk => clk, | ||||||
| 			b_rst => rst, | 			b_rst => rst, | ||||||
| 			b_out => rx_data_valid_i | 			b_out => phy_out.rx_data_valid | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 	rxdvff : process(clk, rst) is | 	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 | ||||||
| 	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 | 	cdc_strobe_rxer_inst : entity work.cdc_strobe | ||||||
| 		port map( | 		port map( | ||||||
|  | |||||||
| @ -10,24 +10,94 @@ | |||||||
| 
 | 
 | ||||||
| library ieee; | library ieee; | ||||||
| use ieee.std_logic_1164.all; | use ieee.std_logic_1164.all; | ||||||
|  | use ieee.numeric_std.all; | ||||||
| 
 | 
 | ||||||
| package trashernet_pkg is | package trashernet_pkg is | ||||||
|  | 	-- ---------------- | ||||||
|  | 	-- Type definitions | ||||||
|  | 	-- ---------------- | ||||||
|  | 
 | ||||||
| 	-- General types | 	-- General types | ||||||
| 	subtype byte is std_logic_vector(7 downto 0); | 	subtype byte        is std_logic_vector(7 downto 0); | ||||||
| 	type byte_vector is array (natural range <>) of byte; | 	type    byte_vector is array (natural range <>) of byte; | ||||||
| 
 | 
 | ||||||
| 	-- MAC specific types | 	-- MAC specific types | ||||||
| 	subtype mac_addr_t is byte_vector(0 to 5); | 	subtype  mac_addr_t         is byte_vector(0 to 5); | ||||||
| 	subtype ethertype_t is byte_vector(0 to 1); | 	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); | ||||||
| 
 | 
 | ||||||
| 	type mac_header_fields is record | 	type mac_header_t is record | ||||||
| 		mac_destination : mac_addr_t;   -- Destination MAC address | 		mac_destination : mac_addr_t;   -- Destination MAC address | ||||||
| 		mac_source      : mac_addr_t;   -- Source MAC address | 		mac_source      : mac_addr_t;   -- Source MAC address | ||||||
| 		mac_ethertype   : ethertype_t;  -- Ethertype or length | 		mac_ethertype   : ethertype_t;  -- Ethertype or length | ||||||
| 	end record mac_header_fields; | 	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; | ||||||
| 
 | 
 | ||||||
| 	-- IP specific types | 	-- IP specific types | ||||||
| 	subtype ip_addr_t is byte_vector(0 to 3); | 	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 | ||||||
|  | 	type configuration_t is record | ||||||
|  | 		mac_address : mac_addr_t;       -- MAC address of this node | ||||||
|  | 	end record configuration_t; | ||||||
|  | 	type ipv4_configuration_t is record | ||||||
|  | 		ip_address  : ip_addr_t;        -- IP address of this node | ||||||
|  | 		gateway     : ip_addr_t;        -- Peer to use when target is outside of subnet | ||||||
|  | 		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 | 	-- PHY interface | ||||||
| 	type phy_in_t is record | 	type phy_in_t is record | ||||||
| @ -48,12 +118,12 @@ package trashernet_pkg is | |||||||
| 
 | 
 | ||||||
| 	-- MAC interface | 	-- MAC interface | ||||||
| 	type mac_in_t is record | 	type mac_in_t is record | ||||||
| 		tx_header      : mac_header_fields; -- TX MAC Header Data | 		tx_header      : mac_header_t;  -- TX MAC Header Data | ||||||
| 		tx_mac_data    : byte;          -- Payload | 		tx_mac_data    : byte;          -- Payload | ||||||
| 		tx_mac_data_en : std_logic;     -- Start (and keep) transmitting a frame | 		tx_mac_data_en : std_logic;     -- Start (and keep) transmitting a frame | ||||||
| 	end record mac_in_t; | 	end record mac_in_t; | ||||||
| 	type mac_out_t is record | 	type mac_out_t is record | ||||||
| 		rx_header         : mac_header_fields; -- RX MAC Header Data | 		rx_header         : mac_header_t; -- RX MAC Header Data | ||||||
| 		rx_mac_header_rcv : std_logic;  -- `rx_mac` header have been received and are valid | 		rx_mac_header_rcv : std_logic;  -- `rx_mac` header have been received and are valid | ||||||
| 
 | 
 | ||||||
| 		rx_mac_data       : byte;       -- Ethernet data (after Ethertype) | 		rx_mac_data       : byte;       -- Ethernet data (after Ethertype) | ||||||
| @ -65,8 +135,202 @@ package trashernet_pkg is | |||||||
| 		rx_mac_crc_error  : std_logic;  -- End of packet, CRC invalid | 		rx_mac_crc_error  : std_logic;  -- End of packet, CRC invalid | ||||||
| 	end record mac_out_t; | 	end record mac_out_t; | ||||||
| 
 | 
 | ||||||
|  | 	-- Ethernet I interface | ||||||
|  | 	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_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` | ||||||
|  | 	end record; | ||||||
|  | 	type ethernet_i_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 | ||||||
|  | 		tx_length      : unsigned(15 downto 0); -- TX payload length in bytes | ||||||
|  | 	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` | ||||||
|  | 	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'); | ||||||
|  | 
 | ||||||
|  | 	-- ARP interface | ||||||
|  | 	type arp_out_t is record | ||||||
|  | 		arp_mac      : mac_addr_t;      -- Resulting MAC address for query | ||||||
|  | 		arp_ok_stb   : std_logic;       -- ARP request successful, `arp_mac` valid | ||||||
|  | 		arp_fail_stb : std_logic;       -- ARP request failed, `arp_mac` invalid | ||||||
|  | 	end record arp_out_t; | ||||||
|  | 	type arp_in_t is record | ||||||
|  | 		arp_ip        : ip_addr_t;      -- IP address to query | ||||||
|  | 		arp_query_stb : std_logic;      -- Request MAC for IP | ||||||
|  | 	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) | ||||||
|  | 
 | ||||||
|  | 		rx_ok_stb     : std_logic;      -- End of packet, checksum OK | ||||||
|  | 		rx_error_stb  : std_logic;      -- End of packet, checksum invalid | ||||||
|  | 
 | ||||||
|  | 		rx_data       : byte;           -- RX data | ||||||
|  | 		rx_data_valid : std_logic;      -- RX data valid strobe | ||||||
|  | 
 | ||||||
|  | 		tx_data_ack   : std_logic;      -- Give next data byte of disable `tx_en` | ||||||
|  | 		tx_ok_stb     : std_logic;      -- Transmission successful | ||||||
|  | 		tx_err_stb    : std_logic;      -- Transmission failed | ||||||
|  | 	end record ipv4_out_t; | ||||||
|  | 	type ipv4_in_t is record | ||||||
|  | 		tx_ip_address : ip_addr_t;      -- Destination IP address | ||||||
|  | 		tx_protocol   : ipv4_protocol;  -- Transport Protocol | ||||||
|  | 		tx_length     : ipv4_length;    -- Length of payload | ||||||
|  | 		tx_data       : byte;           -- TX data | ||||||
|  | 		tx_en         : std_logic;      -- Start and continue transmitting | ||||||
|  | 	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_data       : byte;           -- RX data | ||||||
|  | 		rx_data_valid : std_logic;      -- RX data valid strobe | ||||||
|  | 
 | ||||||
|  | 		tx_data_ack   : std_logic;      -- Give next data byte of disable `tx_en` | ||||||
|  | 		tx_ok_stb     : std_logic;      -- Transmission successful | ||||||
|  | 		tx_err_stb    : std_logic;      -- Transmission failed | ||||||
|  | 	end record ipv4_protocol_out_t; | ||||||
|  | 	type ipv4_protocol_in_t is record | ||||||
|  | 		tx_ip_address : ip_addr_t;      -- Destination IP address | ||||||
|  | 		tx_length     : ipv4_length;    -- Length of payload | ||||||
|  | 
 | ||||||
|  | 		tx_data       : byte;           -- TX data | ||||||
|  | 		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; | ||||||
|  | 
 | ||||||
|  | 	-- 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; | end package trashernet_pkg; | ||||||
| 
 | 
 | ||||||
| package body trashernet_pkg is | package body trashernet_pkg is | ||||||
|  | 	function mac_to_string(constant mac_address : in mac_addr_t) return string is | ||||||
|  | 	begin | ||||||
|  | 		return                          -- | ||||||
|  | 		to_hstring(mac_address(0)) & ":" & -- | ||||||
|  | 		to_hstring(mac_address(1)) & ":" & -- | ||||||
|  | 		to_hstring(mac_address(2)) & ":" & -- | ||||||
|  | 		to_hstring(mac_address(3)) & ":" & -- | ||||||
|  | 		to_hstring(mac_address(4)) & ":" & -- | ||||||
|  | 		to_hstring(mac_address(5))      -- | ||||||
|  | 		; | ||||||
|  | 	end function mac_to_string; | ||||||
| 
 | 
 | ||||||
|  | 	function ip_to_string(constant ip_address : in ip_addr_t) return string is | ||||||
|  | 	begin | ||||||
|  | 		return                          -- | ||||||
|  | 		to_string(to_integer(unsigned(ip_address(0)))) & "." & -- | ||||||
|  | 		to_string(to_integer(unsigned(ip_address(1)))) & "." & -- | ||||||
|  | 		to_string(to_integer(unsigned(ip_address(2)))) & "." & -- | ||||||
|  | 		to_string(to_integer(unsigned(ip_address(3)))) -- | ||||||
|  | 		; | ||||||
|  | 	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 | ||||||
|  | 		ret := '1' when bool else '0'; | ||||||
|  | 		return ret; | ||||||
|  | 	end function to_std_logic; | ||||||
| end package body trashernet_pkg; | end package body trashernet_pkg; | ||||||
|  | |||||||
							
								
								
									
										304
									
								
								trashernet/trashernet_rmii.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								trashernet/trashernet_rmii.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,304 @@ | |||||||
|  | 
 | ||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               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; | ||||||
|  | 
 | ||||||
							
								
								
									
										153
									
								
								trashernet/trashernet_udp.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								trashernet/trashernet_udp.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               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; | ||||||
							
								
								
									
										139
									
								
								trashernet/trashernet_udpprot.vhd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								trashernet/trashernet_udpprot.vhd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | |||||||
|  | -- -------------------------------------------------------------------------- -- | ||||||
|  | --               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