Compare commits

...

17 Commits

Author SHA1 Message Date
7379df7b74 phy: Improve clock accuracy at 0.5 dividers at the cost of some jitter 2024-07-15 13:34:54 +02:00
21a0d0e69a phy_cdc: Allow unlimited clock ratios
Actually, assuming that each clock domain is fast enough for 10 MBit/s,
there is no problem running crazy ratios.
2024-07-06 18:05:00 +02:00
943febcb99 phy_cdc: Fix synchronization behavior
For low ratios of the two clock domains, the slow clock domain would
receive incorrect data as the fast (PHY) clock domain's data output
would have already updated with the first bit of the next byte by
the time the slow (system) clock domain read it.

This fix registers the data in the source clock domain as opposed
to the destination clock domain. This is allowed because the valid
strobe is synchronized in a way to guarantee that data has been
valid for a while when it becomes asserted.

Additionally, `phy_out.rx_data` and `phy_out.rx_data_valid` are no
longer registered in the destination (system) clock domain as this
now introduces unnecessary latency. Also, this was broken the way
it was anyway as `rx_active` and `rx_error` would've also had to be
registered to keep all timings in sync.
2024-07-06 17:58:11 +02:00
da7e329939 des: Fix compatibility with Yosys 2024-06-22 11:19:02 +02:00
4bce111d69 doc: Add synthesized size for PHY-only design 2024-02-04 09:34:36 +01:00
2190f146a0 doc: Add IPv4 modules to doc 2022-11-01 17:41:39 +01:00
91cdb13bb3 demo: Add ICMP ping reply demo design 2022-11-01 17:39:39 +01:00
0db98483dc ipv4: Change syntax to make Synplify happy 2022-11-01 17:10:33 +01:00
6de8d24537 icmp: Implement ICMP echo replies 2022-11-01 16:23:27 +01:00
87bc9ce3d7 ipv4: Add rx_length field 2022-11-01 16:22:59 +01:00
fdcb1cb719 ipv4: Implement IPv4 TX 2022-10-31 23:46:46 +01:00
a0c9df28f1 ipv4: Remove obsolete signals 2022-10-31 23:45:05 +01:00
0d7981f801 ipv4: Default sr to all zeroes to squelch warnings 2022-10-31 17:02:37 +01:00
172a11070b ipv4prot: Implement IPv4 protocol muxing 2022-10-31 16:57:54 +01:00
9d9b3b1cce eth: Fix incorrect TX exit condition 2022-10-31 16:56:17 +01:00
e421ba0dff eth: Fix missing reset for tx sel 2022-10-31 16:55:13 +01:00
277d01737d ipv4: Implement RX 2022-10-30 22:32:47 +01:00
15 changed files with 1462 additions and 44 deletions

View File

@ -6,6 +6,7 @@ Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to c
* Layer 1, Physical: `trashernet_phy`
* Layer 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp`
When writing it, the following were the main design philosophies:
@ -27,6 +28,14 @@ Notes:
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
## Stats
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
| Configuration | Slices | LUT4 | Registers |
| ---------------- | ------ | ---- | --------- |
| `trashernet_phy` | 115 | 188 | 91 |
## License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.

View 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>

View 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" ;

View 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>

View File

@ -182,11 +182,13 @@ architecture eth of top_hwitl is
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 ETH_CONFIG : configuration_t := (
mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"33")
);
constant IP_CONFIG : ip_configuration_t := (
ip_address => (x"C0", x"A8", x"02", x"02")
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;
@ -199,10 +201,10 @@ architecture eth of top_hwitl is
signal mac_in : mac_in_t;
constant PROT_ARP : integer := 0;
constant PROT_IP : integer := 1;
constant PROT_IPV4 : integer := 1;
constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := (
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
PROT_IP => ETHERNET_II_PROTOCOL_IP
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;
@ -211,7 +213,53 @@ architecture eth of top_hwitl is
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);
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
@ -304,28 +352,94 @@ begin
if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then
report "RX ARP";
end if;
if (ethernet_ii_out(PROT_IP).rx_header_rcv) then
report "RX IP";
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_protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_ip_address);
end if;
end if;
end process receiver;
arp_request_test : process(clk, rst) is
udp : process(clk) is
begin
if rst then
arp_in.arp_ip <= (x"C0", x"A8", x"02", x"01");
arp_in.arp_query_stb <= '0';
elsif rising_edge(clk) then
arp_in.arp_query_stb <= not button_n(0);
if arp_out.arp_ok_stb then
report "Found MAC: " & mac_to_string(arp_out.arp_mac);
end if;
if arp_out.arp_fail_stb then
report "ARP failed";
if rising_edge(clk) then
if ipv4_protocol_out(IPROT_UDP).rx_header_rcv then
report "Receive UDP";
end if;
end if;
end process arp_request_test;
end process udp;
tx_udp_p : block
type state_t is (IDLE, TX, DONE);
signal state : state_t;
constant PACKET : byte_vector := (
x"10", x"00", -- Source port
x"04", x"00", -- Destination port
x"00", x"15", -- Length
x"00", x"00", -- Checksum (not used)
x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload
);
signal sr : byte_vector(PACKET'range);
signal cnt : integer range 0 to sr'length - 1;
begin
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: Start TX";
end if;
when TX =>
if ipv4_protocol_out(IPROT_UDP).tx_data_ack then
sr <= sr(sr'low + 1 to sr'high) & x"00";
if cnt = 0 then
state <= DONE;
else
cnt <= cnt - 1;
end if;
end if;
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
report "UDP: TX ERROR";
state <= DONE;
end if;
if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then
report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
end if;
when DONE =>
null;
end case;
end if;
end process tx_udp;
ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0';
ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16);
ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0);
end block tx_udp_p;
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;

248
design/top_pingable.vhd Normal file
View 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;

98
trashernet/fifo.vhd Normal file
View 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;

View File

@ -27,7 +27,7 @@ entity trashernet_arp is
-- Configuration
mac_config : in configuration_t; -- Trashernet MAC configuration
ip_config : in ip_configuration_t; -- Trashernet IP configuration
ip_config : in ipv4_configuration_t; -- Trashernet IP configuration
-- ARP application interface
arp_out : out arp_out_t; -- ARP IF (out from ARP)

View File

@ -120,6 +120,7 @@ begin
arb : process(clk, rst) is
begin
if rst then
sel <= SEL_ETH_I;
elsif rising_edge(clk) then
case state is
@ -140,7 +141,7 @@ begin
end if;
when TXD =>
state <= IDLE when (mac_in.tx_mac_data_en or not mac_out.tx_active);
state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
end case;
end if;
end process arb;

View 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_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length;
sr <= byte_vector'(
x"00",
x"00",
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;

View File

@ -0,0 +1,249 @@
-- -------------------------------------------------------------------------- --
-- 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 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 <= HEADER;
ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '0';
bytecount <= BYTECOUNT_HEADER;
elsif rising_edge(clk) then
ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '0';
if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
state <= HEADER;
bytecount <= BYTECOUNT_HEADER;
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
end if;
if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
if state = HEADER then
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data;
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;
ipv4_out.rx_header_rcv <= '1';
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;
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_ip_address <= sr_source_ip;
ipv4_out.rx_protocol <= sr_protocol;
ipv4_out.rx_length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
end block rx;
tx : block
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
signal state : state_t;
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';
elsif rising_edge(clk) then
arp_in.arp_query_stb <= '0';
ipv4_out.tx_data_ack <= '0';
ipv4_out.tx_err_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;
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;
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';
arp_in.arp_ip <= ipv4_in.tx_ip_address;
end block tx;
end architecture rtl;

View File

@ -0,0 +1,122 @@
-- -------------------------------------------------------------------------- --
-- 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_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_ip_address <= ipv4_out.rx_ip_address;
ipv4_protocol_out(i).rx_length <= ipv4_out.rx_length;
ipv4_protocol_out(i).rx_data_valid <= ipv4_out.rx_data_valid when rx_sel = i else '0';
ipv4_protocol_out(i).rx_error_stb <= ipv4_out.rx_error_stb when rx_sel = i else '0';
ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
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);
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 <= IDLE when (not ipv4_in.tx_en);
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;

View File

@ -253,6 +253,7 @@ begin
begin
if rst then
phy_out.rx_data_valid <= '0';
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
elsif rising_edge(clk) then
phy_out.rx_data_valid <= '0';
@ -296,8 +297,11 @@ begin
-- -------------------------------------------------------------------------
transmitter : block
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1;
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG);
signal tx_state : tx_state_t;
@ -367,6 +371,9 @@ begin
if tx_stb_cnt = 0 then
tx_stb_cnt <= TX_STB_CNT_MAX;
if TX_STB_SKIP_SECOND and bit_stage = '1' then
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
end if;
else
tx_stb_cnt <= tx_stb_cnt - 1;
end if;

View File

@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t;
-- Helper signals
signal rx_data_valid_i : std_logic;
signal rx_data_i : byte;
begin
assert F_CLK_PHY > 2 * F_CLK report "F_CLK_PHY must be at least 2x F_CLK" severity failure;
-- -------------------------------------------------------------------------
-- Drives: PHY clock domain
-- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is
begin
if rst then
@ -61,6 +61,7 @@ begin
end if;
end process rstsync;
-- Operate Trashernet in PHY clock domain
trashernet_phy_inst : entity work.trashernet_phy
generic map(
F_CLK => F_CLK_PHY
@ -75,6 +76,22 @@ begin
tx_n => tx_n
);
-- Latch data in PHY clock domain when valid is strobed
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
-- Therefore, we need to latch it here.
rxdff : process(phy_clk, rst) is
begin
if rst then
rx_data_i <= (others => '0');
elsif rising_edge(phy_clk) then
if phy_phy_out.rx_data_valid then
rx_data_i <= phy_phy_out.rx_data;
end if;
end if;
end process rxdff;
synchronizer_txen_inst : entity work.synchronizer
generic map(
SIZE => 5
@ -98,18 +115,10 @@ begin
a_in => phy_phy_out.rx_data_valid,
b_clk => clk,
b_rst => rst,
b_out => rx_data_valid_i
b_out => phy_out.rx_data_valid
);
rxdvff : process(clk, rst) is
begin
if rst then
phy_out.rx_data_valid <= '0';
elsif rising_edge(clk) then
phy_out.rx_data_valid <= rx_data_valid_i;
phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer
end if;
end process rxdvff;
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
cdc_strobe_rxer_inst : entity work.cdc_strobe
port map(

View File

@ -37,9 +37,11 @@ package trashernet_pkg is
type configuration_t is record
mac_address : mac_addr_t; -- MAC address of this node
end record configuration_t;
type ip_configuration_t is record
ip_address : ip_addr_t; -- IP address of this node
end record ip_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;
-- PHY interface
type phy_in_t is record
@ -83,7 +85,7 @@ package trashernet_pkg is
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_IP : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
type ethernet_i_out_t is record
@ -123,6 +125,7 @@ package trashernet_pkg is
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
@ -132,6 +135,64 @@ package trashernet_pkg is
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
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_out_t is record
rx_ip_address : ip_addr_t; -- Source IP address
rx_protocol : ipv4_protocol; -- Transport Protocol
rx_length : ipv4_length; -- Telegram length (excluding header)
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
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_ip_address : ip_addr_t; -- Source IP address
rx_length : ipv4_length; -- Telegram length (excluding header)
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_header_rcv : std_logic; -- Start of reception
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
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;
-- General helper functions
function to_std_logic(constant bool : boolean) return std_logic;
end package trashernet_pkg;
package body trashernet_pkg is
@ -156,4 +217,11 @@ package body trashernet_pkg is
to_string(to_integer(unsigned(ip_address(3)))) --
;
end function ip_to_string;
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;