Compare commits

..

23 Commits

Author SHA1 Message Date
3ecc6a4ed1 fpga: aps6404l_wb: Fix memory arbitration bug 2024-08-14 19:15:57 +02:00
838a8abe7b tools: Update flasher script to new bus layout 2024-08-09 14:03:00 +02:00
e7d9f391ef fpga: Clean up clocking code 2024-08-09 14:01:16 +02:00
b06ccdda2a fpga: Update UART baud rate to 250 kbaud/s 2024-08-09 11:31:59 +02:00
876693ca5f fpga: uart_wb: Latch received byte to allow late retrieval of data
Else, the CPU would get the part old / half current byte. This
enables boot loader baud rates in excess of 19200 baud.
2024-08-09 11:29:33 +02:00
e561c31691 sw: bootrom: Improve performance and reduce size 2024-08-07 18:01:22 +02:00
49ccbea9e9 fpga: aps6404l device model: Remove unnecessary newlines 2024-08-01 21:11:47 +02:00
eddbcf5f6f fpga: trashernet_phy_wb: Ensure LEDs blink even with constant traffic 2024-08-01 21:10:53 +02:00
74280fb79a fpga: Implement single-instruction prefetching from external RAM 2024-08-01 21:06:14 +02:00
280b354813 fpga: aps6404l_controller: Add generic to select wrap mode 2024-08-01 21:03:12 +02:00
7bece90229 fpga: Cleanly separate build VUnit from bench VUnit 2024-08-01 21:02:32 +02:00
db5a14a2f0 fpga: Add generic FIFO core 2024-07-26 10:55:32 +02:00
e81166a82c fpga: Implement functional Ethernet controller 2024-07-23 19:13:42 +02:00
79b1153df7 env: Makefile: Add support for icetime 2024-07-23 18:53:17 +02:00
0209a23980 fpga: Fix clock constraint for system clock 2024-07-23 18:52:14 +02:00
23c7d0ab20 env: run.py: Remove unnecessary files from compilation for synthesis 2024-07-07 15:58:32 +02:00
07bac62438 tools: Add flash.py 2024-07-07 15:35:09 +02:00
e1672bb712 fpga: Test Trashernet TX 2024-07-07 15:31:41 +02:00
4057f5239e sw: Add bootrom 2024-07-06 19:15:41 +02:00
434a06fa70 env: Replace build script with proper Makefile 2024-07-06 19:15:20 +02:00
dd7cf08b6f fpga: Continue work on full HW support 2024-07-06 19:14:39 +02:00
c7caf2189b doc: Add a copy of the MPL-2.0 2024-07-06 19:06:39 +02:00
b7c1286d76 fpga: Implement basic SERV SoC 2024-06-22 11:28:15 +02:00
43 changed files with 4442 additions and 650 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "fpga/hdl/serv"]
path = fpga/hdl/serv
url = https://github.com/olofk/serv.git
[submodule "fpga/hdl/trashernet"]
path = fpga/hdl/trashernet
url = https://git.notsyncing.net/fpga/trashernet.git

373
LICENSE-MPL-2.0.txt Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@ -12,7 +12,14 @@ You can find a copy of the full license in [LICENSE-CC-BY-SA-4.0.TXT](./LICENSE-
### FPGA
TBD.
The FPGA Source Code Form in `/fpga` is subject to the terms of the Mozilla Public License, v. 2.0.
You can find a copy of the full license in [LICENSE-MPL-2.0.txt](./LICENSE-MPL-2.0.txt).
Note that this design incorporates code from third-party projects, which may come with further restrictions. For details, refer to the individual projects's licensing terms:
* [SERV](https://github.com/olofk/serv/blob/main/LICENSE): ISC License
* [Trashernet](https://git.notsyncing.net/fpga/trashernet/src/branch/master/README.MD#license): Mozilla Public License, v. 2.0.
### SW

4
fpga/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build/**
*.bak
wave.fst
vunit_out

79
fpga/Makefile Normal file
View File

@ -0,0 +1,79 @@
# -------------------------------------------------------------------------- --
# TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
# -------------------------------------------------------------------------- --
# TODO
# -------------------------------------------------------------------------- --
# Author : Markus Koch <markus@notsyncing.net>
# Contributors : None
# License : Mozilla Public License (MPL) Version 2
# -------------------------------------------------------------------------- --
BUILD_DIR=./build
VU_DIR=$(BUILD_DIR)/vunit_out
VU_FLAG=$(VU_DIR)/flag
# Collect VHDL sources using VUnit
SOURCES_VHDL=$(shell VUNIT_SIMULATOR=ghdl ./run_physical.py -o $(VU_DIR) -f 2>/dev/null | sed -n 's/^\w\+, \(.\+\)$$/\1/p')
# Collect Verilog sources using bash
SERV_DIR=hdl/serv
SOURCES_VERILOG=$(SERV_DIR)/rtl/*.v
SOURCES_VERILOG+=$(SERV_DIR)/servant/servant_ram.v $(SERV_DIR)/servant/servant_timer.v
# Miscellaneous sources that when changed must trigger a rebuild
SOURCES_MISC=../sw/bootrom/bootrom.vhex
# Constraints
CONSTRAINTS=constraints.pcf
# HW Settings
YOSYS_DEVICE=u
DEVICE?=up5k # up5k, u4k
PACKAGE?=sg48
# Programmer Settings
PROGRAMMER=ft2232
# ---------------------------------------------------------------------------- #
default: $(BUILD_DIR)/bitstream.bin
$(VU_FLAG): $(SOURCES_VHDL) $(SOURCES_MISC)
VUNIT_SIMULATOR=ghdl ./run_physical.py --compile -o $(VU_DIR)
touch $@
$(BUILD_DIR)/netlist-post-synthesis.json: $(VU_FLAG) $(SOURCES_VERILOG)
# Collect GHDL sources from VUnit
$(eval GHDLINCDIRS=$(shell find "$(VU_DIR)/ghdl/libraries" -maxdepth 1 -mindepth 1 -type d | sed "s/^/-P/" | tr '\n' ' '))
yosys -m ghdl -p "read_verilog $(SOURCES_VERILOG); ghdl --std=08 $(GHDLINCDIRS) design.top; synth_ice40 -abc9 -device $(YOSYS_DEVICE) -top top -json $@"
$(BUILD_DIR)/netlist-post-pnr.asc: $(BUILD_DIR)/netlist-post-synthesis.json $(CONSTRAINTS)
nextpnr-ice40 --$(DEVICE) --package $(PACKAGE) --asc $(BUILD_DIR)/netlist-post-pnr.asc --report $(BUILD_DIR)/report.json --detailed-timing-report --json $(BUILD_DIR)/netlist-post-synthesis.json --pcf $(CONSTRAINTS)
$(BUILD_DIR)/timing.rpt: $(BUILD_DIR)/netlist-post-pnr.asc $(CONSTRAINTS)
icetime -d $(DEVICE) -P $(PACKAGE) -p $(CONSTRAINTS) -mtr $@ $(BUILD_DIR)/netlist-post-pnr.asc
$(BUILD_DIR)/bitstream.bin: $(BUILD_DIR)/netlist-post-pnr.asc
icepack $< $@
flash: $(BUILD_DIR)/bitstream.bin
openFPGALoader --unprotect-flash -f -c $(PROGRAMMER) -b ice40_generic $<
clean:
# Build artifacts
rm -rf $(BUILD_DIR)
# Simulation artifacts
rm -rf vunit_out
rm -rf wave.fst*
simulation:
./run.py
.phony: flash clean simulation
# Useful aliases
compile_vhdl: $(VU_FLAG)
synth: $(BUILD_DIR)/netlist-post-synthesis.json
pnr: $(BUILD_DIR)/netlist-post-pnr.asc
pack: $(BUILD_DIR)/bitstream.bin
timing: $(BUILD_DIR)/timing.rpt

47
fpga/constraints.pcf Normal file
View File

@ -0,0 +1,47 @@
set_io psram_ce_n 13
set_io psram_sclk 9
set_io psram_sio[0] 6
set_io psram_sio[1] 11
set_io psram_sio[2] 12
set_io psram_sio[3] 10
set_io flash_ce_n 16
set_io flash_sclk 15
set_io flash_sio[0] 14
set_io flash_sio[1] 17
set_io flash_sio[2] 18
set_io flash_sio[3] 19
set_io pmod[0] 20
set_io pmod[1] 21
set_io pmod[2] 23
set_io pmod[3] 25
set_io pmod[4] 26
set_io pmod[5] 27
set_io pmod[6] 28
set_io pmod[7] 31
set_io uart_rx 37
set_io uart_tx 36
set_io clk_in 35
#set_io eth_rx_n 38
set_io eth_rx_p 42
set_io eth_tx_n[0] 46
set_io eth_tx_n[1] 47
set_io eth_tx_n[2] 44
set_io eth_tx_n[3] 45
set_io eth_tx_p[0] 48
set_io eth_tx_p[1] 2
set_io eth_tx_p[2] 4
set_io eth_tx_p[3] 3
set_io eth_led_green_n 40
set_io eth_led_orange_n 41
set_io led_user 34
set_frequency clk 25
set_frequency clk_phy 50

View File

@ -0,0 +1,117 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 device_models;
library design;
entity bench_aps6404l_controller is
end entity bench_aps6404l_controller;
architecture bench of bench_aps6404l_controller is
signal rst : std_logic;
signal clk : std_logic;
signal psram_ce_n : std_logic;
signal psram_sclk : std_logic;
signal psram_sio : std_logic_vector(3 downto 0);
signal addr : std_logic_vector(23 downto 0);
signal data_in : std_logic_vector(7 downto 0);
signal data_in_next : std_logic;
signal write_en : std_logic;
signal read_en : std_logic;
signal data_out : std_logic_vector(7 downto 0);
signal data_out_valid : std_logic;
begin
aps6404l_controller_inst : entity design.aps6404l_controller
port map(
clk => clk,
rst => rst,
addr => addr,
data_in => data_in,
data_in_next => data_in_next,
data_out => data_out,
data_out_valid => data_out_valid,
write_en => write_en,
read_en => read_en,
psram_ce_n => psram_ce_n,
psram_sclk => psram_sclk,
psram_sio => psram_sio
);
aps6404l_inst : entity device_models.aps6404l
port map(
ce_n => psram_ce_n,
sclk => psram_sclk,
sio => psram_sio
);
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;
test : process is
begin
rst <= '1';
data_in <= (others => '0');
read_en <= '0';
write_en <= '0';
wait for 1 ns;
rst <= '0';
wait for 3 us;
wait until rising_edge(clk);
addr <= x"ABCDEF";
read_en <= '1';
wait for 2 us;
wait until rising_edge(clk);
read_en <= '0';
wait for 1 us;
wait until rising_edge(clk);
addr <= x"ABCDEF";
write_en <= '1';
for i in 10 to 15 loop
data_in <= std_logic_vector(to_unsigned(160 + i, 8));
loop
wait until rising_edge(clk);
exit when data_in_next = '1';
end loop;
end loop;
write_en <= '0';
wait until rising_edge(clk);
write_en <= '1';
for i in 10 to 15 loop
data_in <= std_logic_vector(to_unsigned(160 + i, 8));
loop
wait until rising_edge(clk);
exit when data_in_next = '1';
end loop;
end loop;
write_en <= '0';
wait;
end process test;
end architecture bench;

View File

@ -0,0 +1,177 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- VUnit test bench for the aps6404l Wishbone IF
-- -------------------------------------------------------------------------- --
-- 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;
library generics;
use generics.wishbone_pkg.all;
library device_models;
use device_models.all;
library vunit_lib;
context vunit_lib.vunit_context;
entity bench_aps6404l_wb is
generic(
runner_cfg : string := runner_cfg_default
);
end entity bench_aps6404l_wb;
architecture RTL of bench_aps6404l_wb is
signal wbi_o : wishbone_slave_out;
signal wbi_i : wishbone_slave_in;
signal wb_o : wishbone_slave_out;
signal wb_i : wishbone_slave_in;
signal clk : std_logic;
signal rst : std_logic;
signal psram_ce_n : std_logic;
signal psram_sclk : std_logic;
signal psram_sio : std_logic_vector(3 downto 0);
begin
aps6404l_wb_inst : entity design.aps6404l_wb
port map(
clk => clk,
rst => rst,
wbi_o => wbi_o,
wbi_i => wbi_i,
wb_o => wb_o,
wb_i => wb_i,
psram_ce_n => psram_ce_n,
psram_sclk => psram_sclk,
psram_sio => psram_sio
);
aps6404l_inst : entity device_models.aps6404l
port map(
ce_n => psram_ce_n,
sclk => psram_sclk,
sio => psram_sio
);
test : process is
procedure bus_write(
signal master_o : out wishbone_master_out;
signal master_i : in wishbone_master_in;
constant address : in wishbone_address;
constant data : in wishbone_data
) is
begin
info("Writing to address 0x" & to_hstring(address));
master_o.we <= '1';
master_o.adr <= address;
master_o.dat <= data;
master_o.sel <= (others => '1');
master_o.cyc <= '1';
master_o.stb <= '1';
loop
wait until rising_edge(clk);
exit when master_i.ack = '1';
end loop;
master_o.cyc <= '0';
master_o.stb <= '0';
end procedure bus_write;
procedure bus_read(
signal master_o : out wishbone_master_out;
signal master_i : in wishbone_master_in;
constant address : in wishbone_address;
variable data : out wishbone_data
) is
begin
info("Reading from address 0x" & to_hstring(address));
master_o.we <= '0';
master_o.adr <= address;
master_o.sel <= (others => '1');
master_o.cyc <= '1';
master_o.stb <= '1';
loop
wait until rising_edge(clk);
exit when master_i.ack = '1';
end loop;
data := master_i.dat;
master_o.cyc <= '0';
master_o.stb <= '0';
end procedure bus_read;
procedure readcheck(signal master_o : out wishbone_master_out;
signal master_i : in wishbone_master_in;
constant address : in wishbone_address;
constant expected : in wishbone_data) is
variable data : wishbone_data;
variable time_start : time;
begin
time_start := now;
bus_read(wbi_i, wbi_o, address, data);
info("Read data: 0x" & to_hstring(data) & " after " & time'image(now - time_start));
check(data = expected, "Data readback incorrect.");
end procedure readcheck;
variable data : wishbone_data;
begin
test_runner_setup(runner, runner_cfg);
wbi_i.sel <= (others => '1');
wbi_i.cyc <= '0';
wbi_i.stb <= '0';
wb_i.cyc <= '0';
wb_i.stb <= '0';
while test_suite loop
info("Resetting DUT");
rst <= '1';
wait for 30 ns;
rst <= '0';
if run("data") then
bus_write(wb_i, wb_o, x"00000000", x"12345678");
bus_read(wb_i, wb_o, x"00000000", data);
info("Read data: 0x" & to_hstring(data));
check(data = x"12345678", "Data readback incorrect.");
wait for 500 ns;
elsif run("instruction") then
bus_write(wb_i, wb_o, x"00000000", x"11223344");
bus_write(wb_i, wb_o, x"00000004", x"55667788");
bus_write(wb_i, wb_o, x"00000008", x"99aabbcc");
bus_write(wb_i, wb_o, x"0000000c", x"ddeeff00");
wait for 1 us;
readcheck(wbi_i, wbi_o, x"00000000", x"11223344");
wait for 1 us;
readcheck(wbi_i, wbi_o, x"00000004", x"55667788");
wait for 1 us;
readcheck(wbi_i, wbi_o, x"00000008", x"99aabbcc");
wait for 1 us;
readcheck(wbi_i, wbi_o, x"0000000c", x"ddeeff00");
wait for 1 us;
end if;
end loop;
test_runner_cleanup(runner);
end process test;
test_runner_watchdog(runner, 1 ms);
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;
end architecture RTL;

View File

@ -0,0 +1,121 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.all;
entity bench_fifo_block is
end entity bench_fifo_block;
architecture RTL of bench_fifo_block is
signal clk : std_logic;
signal rst_a : std_logic;
signal clr : std_logic;
signal data_in : std_logic_vector(7 downto 0);
signal write : std_logic;
signal commit : std_logic;
signal abort : std_logic;
signal full : std_logic;
signal data_out : std_logic_vector(data_in'range);
signal data_first : std_logic;
signal empty : std_logic;
signal read : std_logic;
begin
fifo_block_inst : entity generics.fifo_block
generic map(
SIZE => 15
)
port map(
clk => clk,
rst_a => rst_a,
clr => clr,
data_in => data_in,
write => write,
commit => commit,
abort => abort,
full => full,
data_out => data_out,
data_first => data_first,
empty => empty,
read => read
);
clock_driver : process
constant period : time := 10 ns;
begin
clk <= '0';
wait for period / 2;
clk <= '1';
wait for period / 2;
end process clock_driver;
test : process is
procedure push(constant data : in std_logic_vector(7 downto 0)) is
begin
data_in <= data;
write <= '1';
wait until rising_edge(clk);
write <= '0';
end procedure push;
procedure pop(expected : std_logic_vector(7 downto 0); first : boolean) is
begin
read <= '1';
wait until rising_edge(clk);
report "Read data: " & to_hstring(data_out) & " - " & std_logic'image(data_first);
assert (data_first = '1') = first report "Data first was " & std_logic'image(data_first) & " instead of " & boolean'image(first);
assert data_out = expected report "Data is " & to_hstring(data_out) & ", should be " & to_hstring(expected) severity error;
read <= '0';
end procedure;
procedure commit_data is
begin
commit <= '1';
wait until rising_edge(clk);
commit <= '0';
end procedure commit_data;
begin
rst_a <= '1';
clr <= '0';
write <= '0';
commit <= '0';
abort <= '0';
read <= '0';
wait for 10 ns;
rst_a <= '0';
wait until rising_edge(clk);
report "Pushing 5 items into FIFO";
for i in 1 to 5 loop
push(std_logic_vector(to_unsigned(i, data_in'length)));
assert empty = '1' report "FIFO data present without commit";
end loop;
report "Committing data";
commit_data;
wait until rising_edge(clk);
assert empty = '0' report "Commit did not validate data";
report "Reading data";
for i in 1 to 1 loop
pop(std_logic_vector(to_unsigned(i, data_in'length)), i = 1);
end loop;
std.env.stop(0);
wait;
end process test;
end architecture RTL;

View File

@ -0,0 +1,221 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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;
use std.textio.all;
library device_models;
entity bench_top_manual is
end entity bench_top_manual;
architecture bench of bench_top_manual is
constant UART_BAUD : real := 38400.0;
signal clk : std_logic;
signal rst_n_a : std_logic;
signal uart_txd : std_logic;
signal uart_rxd : std_logic;
signal psram_ce_n : std_logic;
signal psram_sclk : std_logic;
signal psram_sio : std_logic_vector(3 downto 0);
signal rx_p : std_logic := '0';
signal rx_n : std_logic := '1';
signal tx_p : std_logic_vector(3 downto 0);
signal tx_n : std_logic_vector(3 downto 0);
procedure print(text : string) is
variable lb : line;
begin
write(lb, text);
writeline(output, lb);
end procedure print;
procedure print(text : character) is
variable lb : line;
begin
write(lb, text);
write(output, lb.all);
flush(output);
end procedure print;
begin
clock_driver : process
constant period : time := 1 sec / 48000000;
begin
clk <= '0';
wait for period / 2;
clk <= '1';
wait for period / 2;
end process clock_driver;
reset_gen : process is
begin
rst_n_a <= '0';
wait for 40 ns;
rst_n_a <= '1';
wait;
end process reset_gen;
top_inst : entity design.top
port map(
clk_50m => clk,
uart_tx => uart_txd,
uart_rx => uart_rxd,
eth_rx_p => rx_p,
--eth_rx_n => rx_n,
eth_tx_p => tx_p,
eth_tx_n => tx_n,
psram_ce_n => psram_ce_n,
psram_sclk => psram_sclk,
psram_sio => psram_sio
);
aps6404l_inst : entity device_models.aps6404l
port map(
ce_n => psram_ce_n,
sclk => psram_sclk,
sio => psram_sio
);
uart_decoder : process is
constant DELAY : time := (1 sec / UART_BAUD);
variable d : std_logic_vector(7 downto 0);
begin
wait until falling_edge(uart_txd);
wait for 0.5 * DELAY;
for i in 0 to 7 loop
wait for DELAY;
d(i) := uart_txd;
end loop;
wait for 1.0 * DELAY;
print(character'val(to_integer(unsigned(d))));
end process uart_decoder;
loader : process is
procedure uart_tx(d : std_logic_vector(7 downto 0)) is
constant DELAY : time := (1 sec / UART_BAUD);
begin
uart_rxd <= '0';
wait for DELAY;
for i in d'low to d'high loop
uart_rxd <= d(i);
wait for DELAY;
end loop;
uart_rxd <= '1';
wait for DELAY;
end procedure uart_tx;
begin
uart_rxd <= '1';
wait for (1 sec / UART_BAUD) * 10;
report ("Writing ABCD+jalr0 to RAM...");
-- WRITE
uart_tx(x"01");
-- to RAM
uart_tx(x"40");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
-- 8 bytes
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"08");
-- with this data ("ABCD", jalr zero)
uart_tx(x"41");
uart_tx(x"42");
uart_tx(x"43");
uart_tx(x"44");
uart_tx(x"e7");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
report ("Reading back from RAM...");
-- READ
uart_tx(x"02");
-- from RAM
uart_tx(x"40");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
-- 4 bytes ("ABCD")
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"04");
print("RX Data: ");
wait for (1 sec / UART_BAUD) * 10 * 5;
print("");
report ("Jumping to RAM+4...");
-- JUMP
uart_tx(x"03");
-- to RAM (+4, jalr zero)
uart_tx(x"40");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"04");
-- The program will jump back to the loader, so we should be able to read again
wait for 100 us;
report ("Reading from RAM again...");
-- READ
uart_tx(x"02");
-- from RAM
uart_tx(x"40");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
-- 4 bytes
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"00");
uart_tx(x"04");
print("RX Data: ");
wait for (1 sec / UART_BAUD) * 10 * 5;
print("");
wait for 400 us;
report "Done.";
std.env.stop;
wait;
end process loader;
end architecture bench;
-- Commands to test on real HW
-- ---------------------------
-- Dump boot ROM : 02 00 00 00 00 00 00 00 FF
-- Write four bytes to RAM : 01 40 00 00 00 00 00 00 04 01 02 03 04
-- Dump these four bytes : 02 40 00 00 00 00 00 00 04

View File

@ -0,0 +1,267 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 aps6404l_controller is
generic(
TOGGLE_WRAP_BOUNDARY : boolean := false
);
port(
clk : in std_logic; -- Max clock 132 MHz -> 66 MHz SPI
rst : in std_logic; --
addr : in std_logic_vector(23 downto 0);
data_in : in std_logic_vector(7 downto 0);
data_in_next : out std_logic; -- Apply next data word at `data`
data_out : out std_logic_vector(7 downto 0);
data_out_valid : out std_logic;
write_en : in std_logic;
read_en : in std_logic; --
psram_ce_n : out std_logic;
psram_sclk : out std_logic; -- Max clock 66 MHz because of RW mode; Else max clock 90 MHz, else we might violate t_ACLK
psram_sio : inout std_logic_vector(3 downto 0) --
);
end entity aps6404l_controller;
architecture rtl of aps6404l_controller is
signal if_latch : std_logic;
signal if_write : std_logic;
signal if_output : std_logic; -- Actively drive sio
type opmode_t is (COMMAND, READ, WRITE);
signal opmode : opmode_t;
constant SR_SIZE : integer := 16;
type sr_element_vector is array (natural range <>) of std_logic_vector(psram_sio'range);
subtype sr_t is sr_element_vector(0 to SR_SIZE - 1);
subtype sr_cnt_t is integer range -SR_SIZE to SR_SIZE - 1;
signal sr : sr_t;
signal sr_preload : sr_t;
signal sr_preload_cnt : sr_cnt_t;
signal sr_load : std_logic;
signal sr_cnt : sr_cnt_t;
signal active : std_logic;
signal rd_second_nibble : std_logic;
constant RD_WAIT_CNT_MAX : integer := 3 - 1;
signal rd_wait_cnt : integer range 0 to RD_WAIT_CNT_MAX;
type state_t is (QPI_EXIT, SPI_RESET1, SPI_RESET2, SPI_ENABLE_QPI, QPI_SET_BOUNDARY, IDLE, READ, WRITE);
signal state : state_t;
signal requested : std_logic;
begin
clkgen : process(clk, rst) is
begin
if rst then
psram_sclk <= '0';
if_latch <= '0';
if_write <= '0';
elsif rising_edge(clk) then
if_latch <= '0';
if_write <= '0';
if (active) then
psram_sclk <= not psram_sclk;
if psram_sclk then -- rising edge
if_latch <= '1'; -- is the latch edge for both
else -- falling edge
if_write <= '1'; -- is a write edge for both
end if;
else
psram_sclk <= '0';
end if;
end if;
end process clkgen;
sr_p : process(clk, rst) is
begin
if rst then
sr_cnt <= -1;
psram_ce_n <= '1';
if_output <= '0';
active <= '0';
rd_second_nibble <= '0';
data_out_valid <= '0';
data_in_next <= '0';
rd_wait_cnt <= RD_WAIT_CNT_MAX;
elsif rising_edge(clk) then
data_out_valid <= '0';
data_in_next <= '0';
if active then
if if_write then
sr <= sr(sr'low + 1 to sr'high) & x"-";
end if;
if sr_cnt = 0 then
if if_latch then
rd_second_nibble <= not rd_second_nibble;
data_out <= data_out(3 downto 0) & psram_sio;
end if;
case opmode is
when COMMAND =>
if if_write then -- Transaction end only allowed on write edge
active <= '0';
if_output <= '0';
psram_ce_n <= '1';
end if;
when READ =>
if_output <= '0'; -- Configure as input
if if_latch and rd_second_nibble then
if rd_wait_cnt = 0 then
data_out_valid <= '1';
else
rd_wait_cnt <= rd_wait_cnt - 1;
end if;
end if;
when WRITE =>
if if_write and not rd_second_nibble then
sr(sr'low to sr'low + 1) <= (data_in(7 downto 4), data_in(3 downto 0));
data_in_next <= '1';
end if;
end case;
else
if if_latch then
sr_cnt <= sr_cnt - 1;
end if;
end if;
else
rd_wait_cnt <= RD_WAIT_CNT_MAX;
rd_second_nibble <= '0';
if_output <= '0';
if sr_load then
sr <= sr_preload;
sr_cnt <= sr_preload_cnt;
psram_ce_n <= '0';
if_output <= '1';
active <= '1';
end if;
end if;
end if;
end process sr_p;
psram_sio <= sr(sr'low) when if_output else (others => 'Z');
fsm : process(clk, rst) is
function qpi_to_spi(constant DATA : in sr_element_vector) return sr_element_vector is
variable vec : sr_element_vector(0 to DATA'length * 4 - 1);
begin
vec := (others => (others => '-'));
for i in DATA'range loop
for j in 0 to 3 loop
vec(i * 4 + j)(0) := DATA(i)(3 - j);
end loop;
end loop;
return vec;
end function;
function addr_to_nibbles(constant ADDR : in std_logic_vector) return sr_element_vector is
variable vec : sr_element_vector(0 to ADDR'length / 4 - 1);
begin
for i in vec'range loop
vec(i) := ADDR(ADDR'length - (i * 4) - 1 downto ADDR'length - (i + 1) * 4);
end loop;
return vec;
end function addr_to_nibbles;
impure function qpi_xfer(NIBBLES : sr_element_vector; constant OPM : in opmode_t := COMMAND) return boolean is
begin
if (requested) then
if not active and not sr_load then
requested <= '0';
return true;
end if;
else
if not active then
sr_preload <= (others => x"-");
sr_preload(NIBBLES'range) <= NIBBLES;
sr_preload_cnt <= NIBBLES'length - 1;
sr_load <= '1';
opmode <= OPM;
requested <= '1';
end if;
end if;
return false;
end function qpi_xfer;
procedure qpi_stop is
begin
opmode <= COMMAND;
end procedure qpi_stop;
impure function POST_QPI_STATE return state_t is
begin
if TOGGLE_WRAP_BOUNDARY then
return QPI_SET_BOUNDARY;
else
return IDLE;
end if;
end function POST_QPI_STATE;
begin
if rst then
state <= QPI_EXIT;
sr_load <= '0';
requested <= '0';
opmode <= COMMAND;
elsif rising_edge(clk) then
sr_load <= '0';
case state is
when QPI_EXIT =>
state <= SPI_RESET1 when qpi_xfer((x"F", x"5"));
when SPI_RESET1 =>
state <= SPI_RESET2 when qpi_xfer(qpi_to_spi((x"6", x"6")));
when SPI_RESET2 =>
state <= SPI_ENABLE_QPI when qpi_xfer(qpi_to_spi((x"9", x"9")));
when SPI_ENABLE_QPI =>
state <= POST_QPI_STATE when qpi_xfer(qpi_to_spi((x"3", x"5")));
when QPI_SET_BOUNDARY =>
state <= IDLE when qpi_xfer((x"C", x"0"));
when IDLE =>
if read_en then
state <= READ;
elsif write_en then
state <= WRITE;
end if;
when READ =>
state <= IDLE when qpi_xfer(sr_element_vector'(x"0", x"B") & addr_to_nibbles(addr), READ);
if not read_en then
qpi_stop;
end if;
when WRITE =>
state <= IDLE when qpi_xfer(sr_element_vector'(x"0", x"2") & addr_to_nibbles(addr), WRITE);
if not write_en then
qpi_stop;
end if;
end case;
end if;
end process fsm;
end architecture rtl;

View File

@ -0,0 +1,263 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
-- TODO: By default, some (random) value is placed in cached_address, which may corrupt data if this happens to be the
-- first address accessesd after reset.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library generics;
use generics.wishbone_pkg.all;
entity aps6404l_wb is
port(
clk : in std_logic;
rst : in std_logic; --
-- Instruction Wishbone IF (32b read access only, caches one instruction ahead)
wbi_o : out wishbone_slave_out; -- Instruction Wishbone bus (out)
wbi_i : in wishbone_slave_in; -- Instruction Wishbone bus (in)
-- Wishbone IF
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
-- PSRAM IF
psram_ce_n : out std_logic;
psram_sclk : out std_logic;
psram_sio : inout std_logic_vector(3 downto 0) --
);
end entity aps6404l_wb;
architecture rtl of aps6404l_wb is
type apsc_in is record
addr : std_logic_vector(23 downto 0);
data_in : std_logic_vector(7 downto 0);
write_en : std_logic;
read_en : std_logic;
end record apsc_in;
type apsc_out is record
data_in_next : std_logic;
data_out : std_logic_vector(7 downto 0);
data_out_valid : std_logic;
end record apsc_out;
signal apsc_i : apsc_in;
signal apsc_o : apsc_out;
signal cached_address : std_logic_vector(apsc_i.addr'range);
signal cached_data : wishbone_data;
signal api_i : apsc_in;
signal api_o : apsc_out;
signal api_request_count : integer range 0 to 2;
signal api_bit_cnt : integer range 0 to 3;
signal apd_i : apsc_in;
signal apd_o : apsc_out;
signal bit_cnt : integer range 0 to 4;
signal data : std_logic_vector(wb_i.dat'range);
signal ap_sel : integer range 0 to 1;
begin
aps6404l_controller_inst : entity work.aps6404l_controller
port map(
clk => clk,
rst => rst,
addr => apsc_i.addr,
data_in => apsc_i.data_in,
data_in_next => apsc_o.data_in_next,
data_out => apsc_o.data_out,
data_out_valid => apsc_o.data_out_valid,
write_en => apsc_i.write_en,
read_en => apsc_i.read_en,
psram_ce_n => psram_ce_n,
psram_sclk => psram_sclk,
psram_sio => psram_sio
);
ap_arb : process(clk, rst) is
begin
if rst then
ap_sel <= 0;
elsif rising_edge(clk) then
if ap_sel = 0 then
if not (api_i.read_en or api_i.write_en) and (apd_i.read_en or apd_i.write_en) then
ap_sel <= 1;
end if;
else
if (apd_i.read_en nor apd_i.write_en) then -- lock until APD releases
ap_sel <= 0;
end if;
end if;
end if;
end process ap_arb;
ap_mux : process(all) is
constant APSC_IN_DEFAULT : apsc_in := (
addr => (others => '-'),
data_in => (others => '-'),
write_en => '0',
read_en => '0'
);
constant APSC_OUT_DEFAULT : apsc_out := (
data_in_next => '0',
data_out => (others => '-'),
data_out_valid => '0'
);
begin
apsc_i <= APSC_IN_DEFAULT;
api_o <= APSC_OUT_DEFAULT;
apd_o <= APSC_OUT_DEFAULT;
if ap_sel = 0 then
api_o <= apsc_o;
apsc_i <= api_i;
else
apd_o <= apsc_o;
apsc_i <= apd_i;
end if;
end process ap_mux;
instruction_memory_p : process(clk, rst) is
begin
if rst then
wbi_o.ack <= '0';
cached_address <= (others => '1'); -- TODO: Set cached_address to 0xFFFFFF
api_request_count <= 0;
elsif rising_edge(clk) then
wbi_o.ack <= '0';
if wbi_i.cyc and wbi_i.stb then
if wbi_i.adr(cached_address'range) = cached_address then -- cache hit, return current immediately, retrieve next in meantime
wbi_o.dat <= cached_data;
wbi_o.ack <= '1';
api_i.addr <= std_logic_vector(unsigned(wbi_i.adr(apsc_i.addr'range)) + 4);
if not api_i.read_en then -- If we haven't yet triggered the retrieve from memory, start it now
api_request_count <= 1;
end if;
else -- cache miss, retrieve current (return asap) + next
if not api_i.read_en then -- If we haven't yet triggered the retrieve from memory, start it now
api_i.addr <= wbi_i.adr(apsc_i.addr'range);
api_request_count <= 2;
end if;
end if;
end if;
if api_o.data_out_valid then
cached_data <= api_o.data_out & cached_data(cached_data'high downto 8); -- TODO: this assumes the CPU quickly processes the ACK (within one SPI byte)
if api_bit_cnt = 3 then
cached_address <= api_i.addr;
if api_request_count > 0 then
api_request_count <= api_request_count - 1;
end if;
api_bit_cnt <= 0;
else
api_bit_cnt <= api_bit_cnt + 1;
end if;
end if;
if not api_i.read_en then
api_bit_cnt <= 0;
end if;
end if;
end process instruction_memory_p;
api_i.read_en <= '1' when api_request_count > 0 else '0';
api_i.write_en <= '0';
wbi_o.err <= '0';
wbi_o.rty <= '0';
wbi_o.stall <= not wbi_o.ack;
main : process(clk, rst) is
variable start_addr : integer range 0 to 3;
variable stop_addr : integer range 0 to 3;
variable success : std_logic;
variable we : std_logic;
begin
if rst then
wb_o.ack <= '0';
bit_cnt <= 0;
apd_i.read_en <= '0';
apd_i.write_en <= '0';
elsif rising_edge(clk) then
wb_o.ack <= '0';
if bit_cnt = 0 then
apd_i.read_en <= '0';
apd_i.write_en <= '0';
start_addr := 0;
for i in 0 to 3 loop
if wb_i.sel(i) = '1' then
start_addr := i;
exit;
end if;
end loop;
stop_addr := 0;
for i in 3 downto 0 loop
if wb_i.sel(i) = '1' then
stop_addr := i;
exit;
end if;
end loop;
apd_i.addr <= wb_i.adr(apsc_i.addr'length - 1 downto 2) & std_logic_vector(to_unsigned(start_addr, 2));
data <= wb_i.dat;
we := wb_i.we;
if not wb_o.ack and wb_i.stb and wb_i.cyc then
bit_cnt <= 4;
if wb_i.we then
wb_o.ack <= '1'; -- We can ack a write immediately
end if;
end if;
else
success := '0';
if bit_cnt >= (4 - stop_addr) and bit_cnt <= (4 - start_addr) then
apd_i.read_en <= not we;
apd_i.write_en <= we;
else
apd_i.read_en <= '0';
apd_i.write_en <= '0';
success := '1';
end if;
if we and apd_o.data_in_next then
success := '1';
elsif not we and apd_o.data_out_valid then
success := '1';
end if;
if success then
bit_cnt <= bit_cnt - 1;
wb_o.dat <= apd_o.data_out & wb_o.dat(wb_o.dat'high downto 8);
data <= x"--" & data(data'high downto 8);
if (bit_cnt = 1) and (we = '0') then -- We only need to ack reads, writes were already acked in bit_cnt = 0
wb_o.ack <= '1';
end if;
end if;
end if;
end if;
end process main;
apd_i.data_in <= data(apsc_i.data_in'range);
wb_o.err <= '0';
wb_o.rty <= '0';
wb_o.stall <= not wb_o.ack;
end architecture rtl;

57
fpga/hdl/design/pll0.vhd Normal file
View File

@ -0,0 +1,57 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
library IEEE;
use IEEE.std_logic_1164.all;
library generics;
use generics.ice40_components.all;
entity pll0 is
generic(
F_IN : in integer;
F_CLK : in integer;
F_CLK_PHY : in integer
);
port(
clk_in : in std_logic;
clk_out : out std_logic;
clk_out_phy : out std_logic;
locked : out std_logic
);
end pll0;
architecture Structure of pll0 is
signal clk_out_i : std_logic;
begin
-- Not clean, but it works...
ckdiv2 : process(clk_out_phy) is
begin
if rising_edge(clk_out_phy) then
clk_out_i <= not clk_out_i;
end if;
end process ckdiv2;
SB_GB_inst : component SB_GB
port map(
USER_SIGNAL_TO_GLOBAL_BUFFER => clk_out_i,
GLOBAL_BUFFER_OUTPUT => clk_out
);
SB_GB2_inst : component SB_GB
port map(
USER_SIGNAL_TO_GLOBAL_BUFFER => clk_in,
GLOBAL_BUFFER_OUTPUT => clk_out_phy
);
assert F_IN = 50000000 report "clk_in: PLL expects clock different from specified." severity failure;
assert F_CLK = 25000000 report "clk: PLL generates clock different from specified." severity failure;
assert F_CLK_PHY = 50000000 report "clk_phy: PLL generates clock different from specified." severity failure;
end Structure;

View File

@ -0,0 +1,91 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.wishbone_pkg.all;
entity serv_top_vhdl is
port(
clk : in std_logic; -- CPU and bus clock
clr : in std_logic; -- Synchronous clear (CPU reset)
wbi_o : out wishbone_master_out; -- Instruction Wishbone bus (out)
wbi_i : in wishbone_master_in; -- Instruction Wishbone bus (in)
wbd_o : out wishbone_master_out; -- Data Wishbone bus (out)
wbd_i : in wishbone_master_in; -- Data Wishbone bus (in)
irq_timer : in std_logic -- System timer interrupt
);
end entity serv_top_vhdl;
architecture rtl of serv_top_vhdl is
component serv_rf_top
port(
clk : in std_logic;
i_rst : in std_logic;
i_timer_irq : in std_logic;
o_ibus_adr : out std_logic_vector(31 downto 0);
o_ibus_cyc : out std_logic;
i_ibus_rdt : in std_logic_vector(31 downto 0);
i_ibus_ack : in std_logic;
o_dbus_adr : out std_logic_vector(31 downto 0);
o_dbus_dat : out std_logic_vector(31 downto 0);
o_dbus_sel : out std_logic_vector(3 downto 0);
o_dbus_we : out std_logic;
o_dbus_cyc : out std_logic;
i_dbus_rdt : in std_logic_vector(31 downto 0);
i_dbus_ack : in std_logic;
o_ext_rs1 : out std_logic_vector(31 downto 0);
o_ext_rs2 : out std_logic_vector(31 downto 0);
o_ext_funct3 : out std_logic_vector(2 downto 0);
i_ext_rd : in std_logic_vector(31 downto 0);
i_ext_ready : in std_logic;
o_mdu_valid : out std_logic
);
end component serv_rf_top;
signal wbd_o_dat_bv : bit_vector(31 downto 0);
begin
serv_top : serv_rf_top
port map(
clk => clk,
i_rst => clr,
i_timer_irq => irq_timer,
o_ibus_adr => wbi_o.adr,
o_ibus_cyc => wbi_o.cyc,
i_ibus_rdt => wbi_i.dat,
i_ibus_ack => wbi_i.ack,
o_dbus_adr => wbd_o.adr,
o_dbus_dat => wbd_o.dat,
o_dbus_sel => wbd_o.sel,
o_dbus_we => wbd_o.we,
o_dbus_cyc => wbd_o.cyc,
i_dbus_rdt => wbd_i.dat,
i_dbus_ack => wbd_i.ack,
o_ext_rs1 => open,
o_ext_rs2 => open,
o_ext_funct3 => open,
i_ext_rd => (others => '0'),
i_ext_ready => '0',
o_mdu_valid => open
);
--wbd_o.dat <= to_std_logic_vector(wbd_o_dat_bv);
wbi_o.stb <= wbi_o.cyc;
wbi_o.sel <= (others => '1');
wbi_o.we <= '0';
wbi_o.dat <= (others => '0');
wbd_o.stb <= wbd_o.cyc;
end architecture rtl;

View File

@ -0,0 +1,58 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.wishbone_pkg.all;
entity servant_gpio_vhdl is
port(
clk : in std_logic; -- CPU and bus clock
clr : in std_logic; -- Synchronous clear (CPU reset)
wb_o : out wishbone_slave_out; -- Instruction Wishbone bus (out)
wb_i : in wishbone_slave_in; -- Instruction Wishbone bus (in)
output : out std_logic -- System Timer Interrupt
);
end entity servant_gpio_vhdl;
architecture rtl of servant_gpio_vhdl is
component servant_gpio
port(
i_wb_clk : in std_logic;
i_wb_dat : in std_logic;
i_wb_we : in std_logic;
i_wb_cyc : in std_logic;
o_wb_rdt : out std_logic;
o_gpio : out std_logic
);
end component servant_gpio;
begin
servant_gpio_inst : component servant_gpio
port map(
i_wb_clk => clk,
i_wb_dat => wb_i.dat(0),
i_wb_we => wb_i.we,
i_wb_cyc => wb_i.cyc and wb_i.stb,
o_wb_rdt => wb_o.dat(0),
o_gpio => output
);
wb_o.ack <= '1';
wb_o.rty <= '0';
wb_o.err <= '0';
wb_o.stall <= '0';
wb_o.dat(31 downto 1) <= (others => '0');
end architecture rtl;

View File

@ -0,0 +1,90 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.wishbone_pkg.all;
entity servant_ram_vhdl is
generic(
memfile : string := "data/empty.hex";
read_only : boolean := false;
adr_width : integer := 16
);
port(
clk : in std_logic; -- CPU and bus clock
clr : in std_logic; -- Synchronous reset
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
wb_i : in wishbone_slave_in -- Wishbone bus (in)
);
end entity servant_ram_vhdl;
architecture rtl of servant_ram_vhdl is
component servant_ram
generic(
depth : integer;
aw : integer;
memfile : string
);
port(
i_wb_clk : in std_logic;
i_wb_rst : in std_logic;
i_wb_adr : in std_logic_vector;
i_wb_dat : in std_logic_vector;
i_wb_sel : in std_logic_vector;
i_wb_we : in std_logic;
i_wb_cyc : in std_logic;
o_wb_rdt : out std_logic_vector;
o_wb_ack : out std_logic
);
end component servant_ram;
begin
servant_ram_inst : component servant_ram
generic map(
depth => 2 ** adr_width,
aw => adr_width,
memfile => memfile
)
port map(
i_wb_clk => clk,
i_wb_rst => clr,
i_wb_adr => wb_i.adr(adr_width - 1 downto 2),
i_wb_dat => wb_i.dat,
i_wb_sel => wb_i.sel,
i_wb_we => wb_i.we,
i_wb_cyc => wb_i.cyc and wb_i.stb,
o_wb_rdt => wb_o.dat,
o_wb_ack => wb_o.ack
);
wb_o.rty <= '0';
wb_o.err <= '0';
wb_o.stall <= '0';
rowarn : process(clk) is
begin
if rising_edge(clk) then
if wb_i.cyc and wb_i.stb then
if (unsigned(wb_i.adr) > 2 ** adr_width - 1) then
report "ERROR: Out of bounds for " & servant_ram_vhdl'path_name & " @0x" & to_hstring(wb_i.adr) severity error;
end if;
if (wb_i.we = '1' and read_only) then
report "ERROR: Write access to ROM @0x" & to_hstring(wb_i.adr) severity warning;
end if;
end if;
end if;
end process rowarn;
end architecture rtl;

View File

@ -0,0 +1,65 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.wishbone_pkg.all;
entity servant_timer_vhdl is
port(
clk : in std_logic; -- CPU and bus clock
clr : in std_logic; -- Synchronous clear (CPU reset)
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
irq : out std_logic -- System Timer Interrupt
);
end entity servant_timer_vhdl;
architecture rtl of servant_timer_vhdl is
component servant_timer
generic(
WIDTH : integer
);
port(
i_clk : in std_logic;
i_rst : in std_logic;
o_irq : out std_logic;
i_wb_dat : in std_logic_vector;
i_wb_we : in std_logic;
i_wb_cyc : in std_logic;
o_wb_dat : out std_logic_vector
);
end component servant_timer;
begin
servant_timer_inst : component servant_timer
generic map(
WIDTH => 32
)
port map(
i_clk => clk,
i_rst => clr,
o_irq => irq,
i_wb_dat => wb_i.dat,
i_wb_we => wb_i.we,
i_wb_cyc => wb_i.cyc and wb_i.stb,
o_wb_dat => wb_o.dat
);
wb_o.ack <= '1';
wb_o.rty <= '0';
wb_o.err <= '0';
wb_o.stall <= '0';
end architecture rtl;

317
fpga/hdl/design/top.vhd Normal file
View File

@ -0,0 +1,317 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 generics;
use generics.all;
use generics.wishbone_pkg.all;
use generics.ice40_components.all;
entity top is
generic(
-- System configuration
F_IN : integer := 50000000;
F_CLK : integer := 25000000;
F_CLK_PHY : integer := 50000000;
UART_BAUD : integer := 250000
);
port(
clk_in : in std_logic; -- System clock
-- UART
uart_tx : out std_logic; -- UART TX
uart_rx : in std_logic; -- UART RX
-- Trashernet
eth_rx_p : in std_logic; -- Ethernet RX+
--eth_rx_n : in std_logic; -- Ethernet RX-
eth_tx_p : out std_logic_vector(3 downto 0); -- Ethernet TX+
eth_tx_n : out std_logic_vector(3 downto 0); -- Ethernet TX-
-- LEDs
eth_led_green_n : out std_logic;
eth_led_orange_n : out std_logic;
led_user : out std_logic; --
-- PSRAM IF
psram_ce_n : out std_logic;
psram_sclk : out std_logic;
psram_sio : inout std_logic_vector(3 downto 0); --
-- Config Flash
flash_ce_n : out std_logic;
flash_sclk : out std_logic;
flash_sio : inout std_logic_vector(3 downto 0); --
-- PMOD
pmod : inout std_logic_vector(7 downto 0)
);
end entity top;
architecture rtl of top is
-- System
signal pll_locked : std_logic;
signal clk : std_logic;
signal clk_phy : std_logic;
signal rst : std_logic := '1'; -- Asynchronous assert, synchronous release reset
signal clr : std_logic := '1'; -- Fully synchronous reset
signal eth_tx_p_i : std_logic;
signal eth_tx_n_i : std_logic;
signal eth_rx_p_i : std_logic;
signal eth_led_green : std_logic;
signal eth_led_orange : std_logic;
-- System Timer
signal irq_timer : std_logic;
-- # Instruction Bus
-- Wishbone interconnect (master)
signal wbi_masters_o : wishbone_slave_in_vector(0 downto 0);
signal wbi_masters_i : wishbone_slave_out_vector(wbi_masters_o'range);
alias wbi_o is wbi_masters_o(0);
alias wbi_i is wbi_masters_i(0);
-- Wishbone interconnect (slave)
signal wbi_slaves_o : wishbone_master_in_vector(1 downto 0);
signal wbi_slaves_i : wishbone_master_out_vector(wbi_slaves_o'range);
alias wbi_rom_o is wbi_slaves_o(0);
alias wbi_rom_i is wbi_slaves_i(0);
alias wbi_ram_o is wbi_slaves_o(1);
alias wbi_ram_i is wbi_slaves_i(1);
-- Slave address map
constant wishbone_instruction_addresses : wishbone_address_vector := (
0 => x"00000000", -- Boot ROM
1 => x"40000000" -- RAM
);
constant wishbone_instruction_masks : wishbone_address_vector := (
0 => x"C0000000", -- Boot ROM
1 => x"C0000000" -- RAM
);
-- # Data Bus
-- Wishbone interconnect (master)
signal wbd_masters_o : wishbone_slave_in_vector(0 downto 0);
signal wbd_masters_i : wishbone_slave_out_vector(wbd_masters_o'range);
alias wbd_o is wbd_masters_o(0);
alias wbd_i is wbd_masters_i(0);
-- Wishbone interconnect (slave)
signal wbd_slaves_o : wishbone_master_in_vector(3 downto 0);
signal wbd_slaves_i : wishbone_master_out_vector(wbd_slaves_o'range);
alias wb_ram_o is wbd_slaves_o(0);
alias wb_ram_i is wbd_slaves_i(0);
alias wb_timer_o is wbd_slaves_o(1);
alias wb_timer_i is wbd_slaves_i(1);
alias wb_uart_o is wbd_slaves_o(2);
alias wb_uart_i is wbd_slaves_i(2);
alias wb_eth_o is wbd_slaves_o(3);
alias wb_eth_i is wbd_slaves_i(3);
-- Slave address map
constant wishbone_data_addresses : wishbone_address_vector := (
0 => x"40000000", -- RAM
1 => x"80000000", -- Timer
2 => x"81000000", -- UART
3 => x"82000000" -- Eth
);
constant wishbone_data_masks : wishbone_address_vector := (
0 => x"FF000000", -- RAM: Max 16M
1 => x"FF000000", -- Timer
2 => x"FF000000", -- UART
3 => x"FF000000" -- Eth
);
constant IN_SIMULATION : boolean := false --
-- pragma translate_off
or true --
-- pragma translate_on
;
begin
SB_IO_inst : component SB_IO
generic map(
PIN_TYPE => "000000",
IO_STANDARD => "SB_LVDS_INPUT"
)
port map(
PACKAGE_PIN => eth_rx_p,
LATCH_INPUT_VALUE => '1',
CLOCK_ENABLE => '1',
INPUT_CLK => clk_phy,
OUTPUT_CLK => clk_phy,
OUTPUT_ENABLE => '0',
D_OUT_0 => '0',
D_OUT_1 => '0',
D_IN_0 => eth_rx_p_i,
D_IN_1 => open
);
clockgen : if IN_SIMULATION generate
clock_driver : process
constant period : time := 1 sec / real(F_CLK);
begin
clk <= '0';
wait for period / 2;
clk <= '1';
wait for period / 2;
end process clock_driver;
pll_locked <= '1';
else generate
pll_inst : entity work.pll0
generic map(
F_IN => F_IN,
F_CLK => F_CLK,
F_CLK_PHY => F_CLK_PHY
)
port map(
clk_in => clk_in,
clk_out => clk,
clk_out_phy => clk_phy,
locked => pll_locked
);
end generate clockgen;
por : process(clk) is
variable reset_cnt : integer range 0 to 255 := 0;
begin
if rising_edge(clk) then
if reset_cnt = 255 then
rst <= '0';
clr <= '0';
else
reset_cnt := reset_cnt + 1;
rst <= '1';
clr <= '1';
end if;
end if;
end process por;
serv_top_vhdl_inst : entity work.serv_top_vhdl
port map(
clk => clk,
clr => clr,
wbi_o => wbi_o,
wbi_i => wbi_i,
wbd_o => wbd_o,
wbd_i => wbd_i,
irq_timer => irq_timer
);
servant_rom_vhdl_inst : entity work.servant_ram_vhdl
generic map(
memfile => "../sw/bootrom/bootrom.vhex",
read_only => true,
adr_width => 9
)
port map(
clk => clk,
clr => clr,
wb_o => wbi_rom_o,
wb_i => wbi_rom_i
);
aps6404l_wb_inst : entity work.aps6404l_wb
port map(
clk => clk,
rst => rst,
wbi_o => wbi_ram_o,
wbi_i => wbi_ram_i,
wb_o => wb_ram_o,
wb_i => wb_ram_i,
psram_ce_n => psram_ce_n,
psram_sclk => psram_sclk,
psram_sio => psram_sio
);
servant_timer_vhdl_inst : entity work.servant_timer_vhdl
port map(
clk => clk,
clr => clr,
wb_o => wb_timer_o,
wb_i => wb_timer_i,
irq => irq_timer
);
uart_wb_inst : entity work.uart_wb
generic map(
F_CLK => F_CLK,
BAUD_RATE => UART_BAUD
)
port map(
clk => clk,
rst => rst,
wb_o => wb_uart_o,
wb_i => wb_uart_i,
serial_out => uart_tx,
serial_in => uart_rx
);
trashernet_phy_wb_inst : entity work.trashernet_phy_wb
generic map(
F_CLK => F_CLK,
F_CLK_PHY => F_CLK_PHY
)
port map(
clk => clk,
phy_clk => clk_phy,
rst => rst,
wb_o => wb_eth_o,
wb_i => wb_eth_i,
rx_p => eth_rx_p_i,
tx_p => eth_tx_p_i,
tx_n => eth_tx_n_i,
led_rx => eth_led_green,
led_tx => eth_led_orange
);
eth_tx_p <= (others => eth_tx_p_i);
eth_tx_n <= (others => eth_tx_n_i);
wishbone_crossbar_data_inst : entity generics.wishbone_arbiter
port map(
clk => clk,
rst => rst,
masters_o => wbd_masters_o,
masters_i => wbd_masters_i,
slaves_o => wbd_slaves_o,
slaves_i => wbd_slaves_i,
address => wishbone_data_addresses,
mask => wishbone_data_masks
);
wishbone_crossbar_instruction_inst : entity generics.wishbone_arbiter
port map(
clk => clk,
rst => rst,
masters_o => wbi_masters_o,
masters_i => wbi_masters_i,
slaves_o => wbi_slaves_o,
slaves_i => wbi_slaves_i,
address => wishbone_instruction_addresses,
mask => wishbone_instruction_masks
);
eth_led_green_n <= not eth_led_green;
eth_led_orange_n <= not eth_led_orange;
led_user <= not uart_tx or not uart_rx;
flash_ce_n <= 'Z';
flash_sclk <= 'Z';
flash_sio <= (others => 'Z');
pmod <= (others => 'Z');
end architecture rtl;

View File

@ -0,0 +1,262 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 trashernet;
use trashernet.trashernet_pkg.all;
library generics;
use generics.all;
use generics.wishbone_pkg.all;
entity trashernet_phy_wb is
generic(
F_CLK : in integer; -- Clock frequency in Hz
F_CLK_PHY : in integer
);
port(
-- Global
clk : in std_logic; -- Global clock
phy_clk : in std_logic; -- PHY clock
rst : in std_logic; -- Asynchronous reset
-- Wishbone IF
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
-- Ethernet physical signals
rx_p : in std_logic;
tx_p : out std_logic;
tx_n : out std_logic;
-- LEDs
led_tx : out std_logic;
led_rx : out std_logic
);
end entity trashernet_phy_wb;
architecture RTL of trashernet_phy_wb is
-- PHY application interface
signal phy_out : phy_out_t; -- PHY application IF (out)
signal phy_in : phy_in_t; -- PHY application IF (in)
signal mac_out : raw_mac_out_t; -- MAC application IF (out)
signal mac_in : raw_mac_in_t; -- MAC application IF (in)
signal wb_adr : unsigned(2 downto 2);
signal status_register : std_logic_vector(31 downto 0);
signal rx_fifo_read : std_logic;
signal rx_fifo_data : std_logic_vector(7 downto 0);
signal tx_fifo_write : std_logic;
signal tx_fifo_commit : std_logic;
signal tx_fifo_empty : std_logic;
signal tx_fifo_data : std_logic_vector(7 downto 0);
signal rx_fifo_empty : std_logic;
signal rx_bitcount : std_logic_vector(10 downto 0);
signal rx_fifo_block_count : std_logic_vector(rx_bitcount'range);
signal rx_block_next : std_logic;
begin
trashernet_phy_inst : entity trashernet.trashernet_phy_cdc
generic map(
F_CLK => F_CLK,
F_CLK_PHY => F_CLK_PHY
)
port map(
clk => clk,
phy_clk => phy_clk,
rst => rst,
phy_out => phy_out,
phy_in => phy_in,
rx_p => rx_p,
tx_p => tx_p,
tx_n => tx_n
);
trashernet_raw_mac_inst : entity trashernet.trashernet_raw_mac
port map(
clk => clk,
rst => rst,
phy_out => phy_out,
phy_in => phy_in,
mac_out => mac_out,
mac_in => mac_in
);
rx_fifo_inst : entity generics.fifo_block
generic map(
SIZE => 2047
)
port map(
clk => clk,
rst_a => '0',
clr => rst,
data_in => mac_out.rx_mac_data,
write => mac_out.rx_mac_valid,
commit => mac_out.rx_mac_crc_ok,
abort => mac_out.rx_mac_crc_error,
full => open,
data_out => rx_fifo_data,
data_first => open,
empty => rx_fifo_empty,
read => rx_fifo_read,
usage => open
);
fifo_inst : entity generics.fifo
generic map(
SIZE => 31
)
port map(
clk => clk,
rst_a => '0',
clr => rst,
data_in => rx_bitcount,
write => mac_out.rx_mac_crc_ok,
full => open,
data_out => rx_fifo_block_count,
empty => open,
read => rx_block_next,
usage => open
);
rxcount : process(rst, clk) is
begin
if rst then
rx_bitcount <= (others => '0');
elsif rising_edge(clk) then
if mac_out.rx_mac_crc_error or mac_out.rx_mac_crc_ok then
rx_bitcount <= (others => '0');
elsif mac_out.rx_mac_valid then
rx_bitcount <= std_logic_vector(unsigned(rx_bitcount) + 1);
end if;
end if;
end process rxcount;
tx_fifo_inst : entity generics.fifo_block
generic map(
SIZE => 2047
)
port map(
clk => clk,
rst_a => '0',
clr => rst,
data_in => wb_i.dat(7 downto 0),
write => tx_fifo_write,
commit => tx_fifo_commit,
abort => '0',
full => open,
data_out => tx_fifo_data,
data_first => open,
empty => tx_fifo_empty,
read => mac_out.tx_mac_data_ack,
usage => open
);
wb_adr <= unsigned(wb_i.adr(wb_adr'range));
wbif : process(clk, rst) is
begin
if rst then
wb_o.ack <= '0';
rx_fifo_read <= '0';
tx_fifo_write <= '0';
tx_fifo_commit <= '0';
rx_block_next <= '0';
elsif rising_edge(clk) then
wb_o.ack <= '0';
rx_fifo_read <= '0';
tx_fifo_write <= '0';
tx_fifo_commit <= '0';
rx_block_next <= '0';
if (wb_i.cyc and wb_i.stb) then
wb_o.ack <= '1';
if wb_adr = 0 then
wb_o.dat <= status_register;
if wb_o.ack = '0' then
if wb_i.we = '1' then
tx_fifo_commit <= wb_i.dat(2);
rx_block_next <= wb_i.dat(1);
end if;
end if;
else
wb_o.dat <= (others => '0');
if wb_o.ack = '0' then
if wb_i.we = '1' then
tx_fifo_write <= '1';
else
rx_fifo_read <= '1';
end if;
end if;
wb_o.dat(7 downto 0) <= rx_fifo_data;
end if;
end if;
end if;
end process wbif;
wb_o.err <= '0';
wb_o.rty <= '1';
wb_o.stall <= '0';
mac_in.tx_mac_data_en <= not tx_fifo_empty; -- FIXME: this does not care about inter-packet gaps indicated by `data_first`
mac_in.tx_mac_data <= tx_fifo_data;
status_register <= ("0" & x"0" & rx_fifo_block_count & -- 31..16
x"000" & "0" & (not tx_fifo_empty) & (not rx_fifo_empty) & phy_out.carrier_detect);
ledstretch : process(clk) is
constant CMAX : integer := integer(0.1 / (1.0 / real(F_CLK)));
constant CMIN : integer := -integer(0.1 / (1.0 / real(F_CLK)));
variable cnt : integer range CMIN to CMAX;
begin
if rising_edge(clk) then
if cnt = CMIN then
led_rx <= phy_out.carrier_detect;
if phy_out.rx_active then
cnt := CMAX;
led_rx <= not phy_out.carrier_detect;
end if;
elsif cnt = 0 then
led_rx <= phy_out.carrier_detect;
cnt := cnt - 1;
else
cnt := cnt - 1;
end if;
end if;
end process ledstretch;
ledstretch2 : process(clk) is
constant CMAX : integer := integer(0.1 / (1.0 / real(F_CLK)));
constant CMIN : integer := -integer(0.1 / (1.0 / real(F_CLK)));
variable cnt : integer range CMIN to CMAX;
begin
if rising_edge(clk) then
if cnt = CMIN then
if phy_out.tx_active then
cnt := CMAX;
led_tx <= '1';
end if;
elsif cnt = 0 then
led_tx <= '0';
cnt := cnt - 1;
else
cnt := cnt - 1;
end if;
end if;
end process ledstretch2;
end architecture RTL;

125
fpga/hdl/design/uart_wb.vhd Normal file
View File

@ -0,0 +1,125 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
-- ADDR0: RD: [...] TX_BUSY TX_DATA_FULL RX_DATA_EMPTY
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library generics;
use generics.all;
use generics.wishbone_pkg.all;
entity uart_wb is
generic(
F_CLK : integer;
BAUD_RATE : integer := 115200
);
port(
clk : in std_logic;
rst : in std_logic;
-- Wishbone IF
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
serial_out : out std_logic;
serial_in : in std_logic
);
end entity uart_wb;
architecture rtl of uart_wb is
signal wb_adr : unsigned(2 downto 0);
signal status_register : std_logic_vector(31 downto 0);
signal in_data : std_logic_vector(7 downto 0);
signal in_data_latched : std_logic_vector(in_data'range);
signal in_data_valid : std_logic;
signal in_data_available : std_logic;
signal out_data : std_logic_vector(in_data'range);
signal out_data_valid : std_logic;
signal out_data_latched : std_logic;
signal out_busy : std_logic;
begin
uart_tx_inst : entity generics.uart_tx
generic map(
F_CLK => F_CLK,
BAUD_RATE => BAUD_RATE
)
port map(
clk => clk,
rst_a => rst,
data => out_data,
data_valid => out_data_valid,
data_latched => out_data_latched,
busy => out_busy,
serial_out => serial_out
);
uart_rx_inst : entity generics.uart_rx
generic map(
F_CLK => F_CLK,
BAUD_RATE => BAUD_RATE
)
port map(
clk => clk,
rst_a => rst,
data => in_data,
data_valid => in_data_valid,
serial_in_a => serial_in
);
wb_adr <= unsigned(wb_i.adr(wb_adr'range));
status_register <= x"000000" & "00000" & out_busy & out_data_valid & (not in_data_available);
wb_if : process(clk, rst) is
begin
if rst then
in_data_available <= '0';
out_data_valid <= '0';
wb_o.ack <= '0';
elsif rising_edge(clk) then
wb_o.ack <= '0';
if out_data_latched then
out_data_valid <= '0';
end if;
if (wb_i.CYC and wb_i.STB) then
wb_o.ack <= '1';
if wb_adr = 0 then -- status reg
wb_o.dat <= status_register;
else -- data reg (write to transmit, read to retrieve)
if wb_i.we then
out_data <= wb_i.dat(out_data'range);
out_data_valid <= '1';
else
wb_o.dat <= x"000000" & in_data_latched;
in_data_available <= '0';
end if;
end if;
end if;
if in_data_valid then
in_data_available <= '1';
in_data_latched <= in_data;
end if;
end if;
end process wb_if;
wb_o.err <= '0';
wb_o.stall <= '0';
wb_o.rty <= '0';
end architecture rtl;

View File

@ -0,0 +1,176 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 std.textio.all;
entity aps6404l is
generic(
LOG_EN : boolean := true
);
port(
ce_n : in std_logic;
sclk : in std_logic;
sio : inout std_logic_vector(3 downto 0)
);
end entity aps6404l;
architecture bench of aps6404l is
procedure print(text : string) is
variable buf : line;
begin
if not LOG_EN then
return;
end if;
write(buf, string'("aps6404l: "));
write(buf, text);
WriteLine(OUTPUT, buf);
end procedure print;
begin
test : process is
type rx_state_t is (COMMAND, READ, WRITE, COMPLETE);
type byte_vector is array (natural range <>) of std_logic_vector(7 downto 0);
variable mem : byte_vector(0 to 1023);
variable bytes : byte_vector(0 to 32);
variable cnt, bytecnt : integer;
variable state : rx_state_t;
variable mode : integer := 0;
variable wrap_boundary : boolean := false;
variable dout : std_logic_vector(7 downto 0);
variable addr : integer;
variable sio_temp : std_logic_vector(sio'range);
procedure handle(command_edge : boolean) is
begin
if command_edge then
case state is
when COMMAND =>
case bytes(0) is
when x"66" =>
print(" SPI reset enter");
state := COMPLETE;
when x"99" =>
print(" SPI reset execute");
mode := 0;
wrap_boundary := false;
state := COMPLETE;
when x"35" =>
print(" QPI enter");
mode := 1;
state := COMPLETE;
when x"F5" =>
print(" QPI exit");
mode := 0;
state := COMPLETE;
when x"C0" =>
print(" Wrap Boundary Toggle");
wrap_boundary := not wrap_boundary;
state := COMPLETE;
when x"0B" =>
print(" QPI Read Slow");
state := READ;
when x"EB" =>
print(" QPI Read Fast");
state := READ;
when x"02" =>
print(" QPI Write (1)");
state := WRITE;
when x"38" =>
print(" QPI Write (2)");
state := WRITE;
when others =>
print(" Unknown command: 0x" & to_hstring(bytes(0)));
state := COMPLETE;
end case;
when READ =>
if bytecnt = 3 then
print(" Addr: " & to_hstring(bytes(1)) & to_hstring(bytes(2)) & to_hstring(bytes(3)));
addr := to_integer(unsigned(std_logic_vector'(bytes(1), bytes(2), bytes(3))));
elsif bytecnt > 4 then
dout := mem(addr);
print(" Read " & integer'image(addr) & ": " & to_hstring(mem(addr)));
addr := addr + 1;
end if;
when WRITE =>
if bytecnt = 3 then
print(" Addr: " & to_hstring(bytes(1)) & to_hstring(bytes(2)) & to_hstring(bytes(3)));
addr := to_integer(unsigned(std_logic_vector'(bytes(1), bytes(2), bytes(3))));
elsif bytecnt > 3 then
print(" Write " & integer'image(addr) & ": " & to_hstring(bytes(bytecnt)));
mem(addr) := bytes(bytecnt);
addr := addr + 1;
end if;
when COMPLETE =>
null;
end case;
bytecnt := bytecnt + 1;
end if;
if state = READ and bytecnt > 4 then
sio_temp := dout(7 downto 4) when command_edge else dout(3 downto 0);
sio <= sio_temp after 5 ns;
end if;
end procedure handle;
begin
sio <= (others => 'Z');
wait until ce_n = '0';
print("Selected.");
bytes := (others => (others => 'U'));
cnt := 0;
bytecnt := 0;
state := COMMAND;
loop
wait on sclk, ce_n;
if mode = 0 then -- SPI
if falling_edge(sclk) then
bytes(cnt / 8)(7 - (cnt mod 8)) := sio(0);
cnt := cnt + 1;
if cnt mod 4 = 0 then
handle(cnt mod 8 = 0);
end if;
end if;
else -- QPI
if falling_edge(sclk) then
bytes(cnt / 2)((1 - (cnt mod 2) + 1) * 4 - 1 downto (1 - (cnt mod 2)) * 4) := sio;
cnt := cnt + 1;
end if;
if falling_edge(sclk) then
handle(cnt mod 2 = 0);
end if;
end if;
exit when ce_n = '1';
end loop;
print("Deselected.");
end process test;
end architecture bench;

106
fpga/hdl/generics/fifo.vhd Normal file
View File

@ -0,0 +1,106 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 fifo is
generic(
SIZE : integer := 15 -- Entries in the FIFO (of size data), SIZE=(n^2)-1 recommended
);
port(
-- generic ports
clk : in std_logic; -- System clock
rst_a : in std_logic; -- Asynchronous reset
clr : in std_logic; -- Remove all entries
-- FIFO in
data_in : in std_logic_vector; -- Data input into FIFO
write : in std_logic; -- Data is latched when `data_in_valid and not full`
full : out std_logic; -- FIFO is full
-- FIFO out
data_out : out std_logic_vector; -- Data output from FIFO
empty : out std_logic; -- FIFO is empty. '0' means "data is available".
read : in std_logic; -- Acknowledge that `data_out` was read. The word is removed and next one shifted from FIFO when `read and not empty`.
-- FIFO meta
usage : out integer range 0 to SIZE -- Usage counter
);
end fifo;
architecture rtl of fifo is
constant MEMORY_WIDTH : integer := data_in'length;
subtype pointer_t is integer range 0 to SIZE - 1;
type memory_t is array (pointer_t) of std_logic_vector(MEMORY_WIDTH - 1 downto 0);
signal memory : memory_t;
signal read_pointer : pointer_t;
signal write_pointer : pointer_t;
signal read_pointer_last : pointer_t;
begin
fifo_p : process(rst_a, clk) is
variable succesful_read : std_logic;
variable succesful_write : std_logic;
variable write_pointer_next : pointer_t;
variable read_pointer_next : pointer_t;
begin
if rst_a then
read_pointer <= 0;
write_pointer <= 0;
read_pointer_last <= SIZE - 1;
usage <= 0;
elsif rising_edge(clk) then
succesful_read := read and not empty;
succesful_write := write and (not full or succesful_read);
write_pointer_next := write_pointer;
read_pointer_next := read_pointer;
if succesful_read then
read_pointer_next := (read_pointer_next + 1) mod SIZE;
read_pointer_last <= read_pointer;
end if;
if succesful_write then
memory(write_pointer) <= data_in;
write_pointer_next := (write_pointer_next + 1) mod SIZE;
end if;
if succesful_write xor succesful_read then
if succesful_write then
usage <= usage + 1;
else
usage <= usage - 1;
end if;
end if;
read_pointer <= read_pointer_next;
write_pointer <= write_pointer_next;
if clr then
read_pointer <= 0;
write_pointer <= 0;
read_pointer_last <= SIZE - 1;
usage <= 0;
end if;
end if;
end process fifo_p;
full <= '1' when read_pointer_last = write_pointer else '0';
empty <= '1' when (read_pointer = write_pointer) else '0';
data_out <= memory(read_pointer);
end rtl;

View File

@ -0,0 +1,127 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
-- Tech hint: Width of FIFO will be width of `data_in` + 1.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity fifo_block is
generic(
SIZE : integer := 15 -- Entries in the FIFO (of size data), SIZE=(n^2)-1 recommended
);
port(
-- generic ports
clk : in std_logic; -- System clock
rst_a : in std_logic; -- Asynchronous reset
clr : in std_logic; -- Remove all entries
-- FIFO in
data_in : in std_logic_vector; -- Data input into FIFO
write : in std_logic; -- Data is latched when `data_in_valid and not full`
commit : in std_logic; -- Finalize block
abort : in std_logic; -- Abort current block
full : out std_logic; -- FIFO is full
-- FIFO out
data_out : out std_logic_vector; -- Data output from FIFO
data_first : out std_logic; -- Currently visible word is first in packet
empty : out std_logic; -- FIFO is empty. '0' means "data is available".
read : in std_logic; -- Acknowledge that `data_out` was read. The word is removed and next one shifted from FIFO when `read and not empty`.
-- FIFO meta
usage : out integer range 0 to SIZE -- Usage counter
);
end fifo_block;
architecture rtl of fifo_block is
constant MEMORY_WIDTH : integer := data_in'length + 1;
subtype pointer_t is integer range 0 to SIZE - 1;
type memory_t is array (pointer_t) of std_logic_vector(MEMORY_WIDTH - 1 downto 0);
signal memory : memory_t;
signal read_pointer : pointer_t;
signal write_pointer : pointer_t;
signal read_pointer_last : pointer_t;
signal write_pointer_committed : pointer_t;
signal current_is_first : std_logic;
begin
fifo : process(rst_a, clk) is
variable succesful_read : std_logic;
variable succesful_write : std_logic;
variable write_pointer_next : pointer_t;
variable read_pointer_next : pointer_t;
begin
if rst_a then
read_pointer <= 0;
write_pointer <= 0;
read_pointer_last <= SIZE - 1;
write_pointer_committed <= 0;
current_is_first <= '1';
usage <= 0;
elsif rising_edge(clk) then
succesful_read := read and not empty;
succesful_write := write and (not full or succesful_read);
write_pointer_next := write_pointer;
read_pointer_next := read_pointer;
if succesful_read then
read_pointer_next := (read_pointer_next + 1) mod SIZE;
read_pointer_last <= read_pointer;
end if;
if succesful_write then
memory(write_pointer) <= current_is_first & data_in;
write_pointer_next := (write_pointer_next + 1) mod SIZE;
current_is_first <= '0';
end if;
if succesful_write xor succesful_read then
if succesful_write then
usage <= usage + 1;
else
usage <= usage - 1;
end if;
end if;
if commit then
write_pointer_committed <= write_pointer;
current_is_first <= '1';
elsif abort then
write_pointer_next := write_pointer_committed;
current_is_first <= '1';
end if;
read_pointer <= read_pointer_next;
write_pointer <= write_pointer_next;
if clr then
read_pointer <= 0;
write_pointer <= 0;
read_pointer_last <= SIZE - 1;
write_pointer_committed <= 0;
current_is_first <= '1';
usage <= 0;
end if;
end if;
end process fifo;
full <= '1' when read_pointer_last = write_pointer else '0';
empty <= '1' when (read_pointer = write_pointer_committed) else '0';
data_out <= memory(read_pointer)(data_out'range);
data_first <= memory(read_pointer)(MEMORY_WIDTH - 1);
end rtl;

View File

@ -0,0 +1,125 @@
library ieee;
use ieee.std_logic_1164.all;
package ice40_components is
component SB_PLL40_PAD
generic(
FEEDBACK_PATH : string;
DIVR : std_logic_vector(3 downto 0);
DIVF : std_logic_vector(6 downto 0);
DIVQ : std_logic_vector(2 downto 0);
FILTER_RANGE : std_logic_vector(2 downto 0)
);
port(
RESETB : in std_logic;
BYPASS : in std_logic;
PACKAGEPIN : in std_logic;
PLLOUTCORE : out std_logic;
PLLOUTGLOBAL : out std_logic
);
end component SB_PLL40_PAD;
component SB_PLL40_2_PAD
generic(
FEEDBACK_PATH : string;
DIVR : std_logic_vector(3 downto 0);
DIVF : std_logic_vector(6 downto 0);
DIVQ : std_logic_vector(2 downto 0);
FILTER_RANGE : std_logic_vector(2 downto 0)
);
port(
RESETB : in std_logic;
BYPASS : in std_logic;
PACKAGEPIN : in std_logic;
PLLOUTCOREA : out std_logic;
PLLOUTCOREB : out std_logic;
PLLOUTGLOBALA : out std_logic;
PLLOUTGLOBALB : out std_logic
);
end component SB_PLL40_2_PAD;
component SB_PLL40_2_CORE
generic(
FEEDBACK_PATH : string;
DIVR : std_logic_vector(3 downto 0);
DIVF : std_logic_vector(6 downto 0);
DIVQ : std_logic_vector(2 downto 0);
FILTER_RANGE : std_logic_vector(2 downto 0)
);
port(
RESETB : in std_logic;
BYPASS : in std_logic;
REFERENCECLK : in std_logic;
PLLOUTCOREA : out std_logic;
PLLOUTCOREB : out std_logic
);
end component SB_PLL40_2_CORE;
component SB_PLL40_CORE
generic(
FEEDBACK_PATH : string := "SIMPLE";
-- DELAY_ADJUSTMENT_MODE_FEEDBACK : string := "FIXED";
-- DELAY_ADJUSTMENT_MODE_RELATIVE : string := "FIXED";
-- PLLOUT_SELECT : string := "GENCLK";
-- SHIFTREG_DIV_MODE : std_logic;
-- FDA_FEEDBACK : std_logic_vector(3 downto 0) := "1111";
-- FDA_RELATIVE : std_logic_vector(3 downto 0) := "1111";
DIVR : std_logic_vector(3 downto 0);
DIVF : std_logic_vector(6 downto 0);
DIVQ : std_logic_vector(2 downto 0);
FILTER_RANGE : std_logic_vector(2 downto 0)
-- ENABLE_ICEGATE : std_logic := '0';
-- TEST_MODE : std_logic := '0'
);
port(
REFERENCECLK : in std_logic;
PLLOUTCORE : out std_logic;
PLLOUTGLOBAL : out std_logic;
-- EXTFEEDBACK : in std_logic;
-- DYNAMICDELAY : in std_logic_vector(7 downto 0);
LOCK : out std_logic;
BYPASS : in std_logic;
RESETB : in std_logic
-- LATCHINPUTVALUE : in std_logic;
-- SDO : out std_logic;
-- SDI : in std_logic;
-- SCLK : in std_logic
);
end component SB_PLL40_CORE;
component SB_HFOSC
generic(
CLKHF_DIV : string
);
port(
CLKHFPU : in std_logic;
CLKHFEN : in std_logic;
CLKHF : out std_logic
);
end component SB_HFOSC;
COMPONENT SB_IO IS
GENERIC(
PIN_TYPE : std_logic_vector(5 downto 0) := "000000";
IO_STANDARD : string := "SB_LVDS_INPUT"
);
PORT(
PACKAGE_PIN : in std_logic;
LATCH_INPUT_VALUE : in std_logic;
CLOCK_ENABLE : in std_logic;
INPUT_CLK : in std_logic;
OUTPUT_CLK : in std_logic;
OUTPUT_ENABLE : in std_logic;
D_OUT_0 : in std_logic;
D_OUT_1 : in std_logic;
D_IN_0 : out std_logic;
D_IN_1 : out std_logic
);
END COMPONENT SB_IO;
component SB_GB
port(
USER_SIGNAL_TO_GLOBAL_BUFFER : in std_logic;
GLOBAL_BUFFER_OUTPUT : out std_logic);
end component;
end package ice40_components;

View File

@ -0,0 +1,55 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 synchronizer is
generic(
init_value : std_logic := '0'; -- Default value of all synchronized signals after reset (ignored when `init_value_v` is set)
init_value_v : std_logic_vector := ""; -- Set this reset the bits of the vector individually, leave "" to use `init_value` for all.
stages : natural := 2 -- Number of synchronizer flip flops per channel
);
port(
clk : in std_logic; -- Synchronizer clock
rst : in std_logic; -- Asynchronous reset
data_in_a : in std_logic_vector; -- Asynchronous signal input
data_out : out std_logic_vector -- Synchronized signal output
);
end entity synchronizer;
architecture rtl of synchronizer is
type synchronizer_chain_t is array (stages - 1 downto 0) of std_logic_vector(data_in_a'range);
signal synchronizer_chain : synchronizer_chain_t;
begin
assert ((init_value_v'length = 0) or (init_value_v'length = data_in_a'length)) report "init_value_v must have the same width as data_in_a when used" severity error;
sync_p : process(clk, rst) is
begin
if (rst = '1') then
if (init_value_v'length = 0) then
synchronizer_chain <= (others => (others => init_value));
else
synchronizer_chain <= (others => init_value_v);
end if;
elsif (rising_edge(clk)) then
for i in synchronizer_chain'high - 1 downto synchronizer_chain'low loop
synchronizer_chain(i + 1) <= synchronizer_chain(i);
end loop;
synchronizer_chain(0) <= data_in_a;
end if;
end process sync_p;
data_out <= synchronizer_chain(synchronizer_chain'high);
end architecture rtl;

View File

@ -0,0 +1,99 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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 uart_rx is
generic(
F_CLK : natural := 50_000_000; -- System clock speed
BAUD_RATE : natural := 115_200 -- Desired baudrate
);
port(
clk : in std_logic; -- System clock @ F_CLK
rst_a : in std_logic; -- Asynchronous reset
data : out std_logic_vector; -- Receive payload word (width determines width of UART payload)
data_valid : out std_logic; -- Data is valid
serial_in_a : in std_logic -- UART RX line (asynchronous) @ BAUDRATE
);
end entity uart_rx;
architecture rtl of uart_rx is
signal serial_in : std_logic;
constant BITCLK_MAX : integer := integer(real(F_CLK) / real(BAUD_RATE));
constant BITCLK_START : integer := BITCLK_MAX / 2;
signal bitclk_cnt : integer range 0 to BITCLK_MAX;
signal bit_stb : std_logic;
constant BITCNT_MAX : integer := 10;
signal cnt : integer range 0 to BITCNT_MAX;
signal busy : std_logic;
begin
synchronizer_rx_inst : entity work.synchronizer
generic map(
init_value => '1'
)
port map(
clk => clk,
rst => rst_a,
data_in_a(0) => serial_in_a,
data_out(0) => serial_in
);
rx : process(clk, rst_a) is
begin
if rst_a then
cnt <= 0;
data_valid <= '0';
elsif rising_edge(clk) then
data_valid <= '0';
if (cnt = 0 and serial_in = '0') or bit_stb = '1' then
if cnt = BITCNT_MAX then
cnt <= 0;
data_valid <= '1';
else
data <= serial_in & data(7 downto 1);
cnt <= cnt + 1;
end if;
end if;
end if;
end process rx;
busy <= '1' when cnt /= 0 else '0';
bitclk : process(clk, rst_a) is
begin
if rst_a then
bit_stb <= '0';
bitclk_cnt <= 0;
elsif rising_edge(clk) then
bit_stb <= '0';
if not busy then
bitclk_cnt <= BITCLK_START;
else
if bitclk_cnt = 0 then
bitclk_cnt <= BITCLK_MAX;
bit_stb <= '1';
else
bitclk_cnt <= bitclk_cnt - 1;
end if;
end if;
end if;
end process bitclk;
end architecture rtl;

View File

@ -0,0 +1,96 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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.round;
entity uart_tx is
generic(
F_CLK : integer := 50_000_000; -- System clock speed
BAUD_RATE : integer := 115_200 -- Desired baudrate
);
port(
clk : in std_logic; -- System clock @ F_CLK
rst_a : in std_logic; -- Asynchronous reset
data : in std_logic_vector; -- Data to send (width determines width of UART payload)
data_valid : in std_logic; -- Data is valid, TX at next opportunity
data_latched : out std_logic; -- The data word has been latched, go ahead and apply the next one, or negate data_valid to end the transaction after the current word
busy : out std_logic; -- The core is busy transmitting a data word
serial_out : out std_logic -- UART TX line
);
end entity uart_tx;
architecture rtl of uart_tx is
constant COUNTER_MAX : natural := 10;
signal sr : std_logic_vector((data'length + 1) - 1 downto 0);
signal bit_cnt : integer range 0 to COUNTER_MAX;
constant BITCLK_MAX : integer := integer(real(F_CLK) / real(BAUD_RATE));
signal bitclk_cnt : integer range 0 to BITCLK_MAX;
signal bit_stb : std_logic;
begin
tx_fsm : process(clk, rst_a) is
begin
if (rst_a = '1') then
sr <= (others => '1');
bit_cnt <= 0;
data_latched <= '0';
elsif (rising_edge(clk)) then
data_latched <= '0';
if ((bit_stb = '1') or (bit_cnt = 0)) then
sr <= '1' & sr(sr'high downto 1);
if (bit_cnt = 0) then
if (data_valid = '1') then
data_latched <= '1';
sr <= data & '0';
bit_cnt <= COUNTER_MAX;
end if;
else
bit_cnt <= bit_cnt - 1;
end if;
end if;
end if;
end process tx_fsm;
serial_out <= sr(0); -- @suppress PID1: Not a readback signal
busy <= '1' when (bit_cnt /= 0) or (data_valid = '1') else '0';
bitclk : process(clk, rst_a) is
begin
if rst_a then
bit_stb <= '0';
bitclk_cnt <= BITCLK_MAX;
elsif rising_edge(clk) then
bit_stb <= '0';
if not busy then
bitclk_cnt <= BITCLK_MAX;
else
if bitclk_cnt = 0 then
bitclk_cnt <= BITCLK_MAX;
bit_stb <= '1';
else
bitclk_cnt <= bitclk_cnt - 1;
end if;
end if;
end if;
end process bitclk;
end architecture rtl;

View File

@ -0,0 +1,136 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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.wishbone_pkg.all;
entity wishbone_arbiter is
generic(
ASYNC : boolean := true; -- Generate the switch as a fully asynchronous circuit when false, register outputs when true.
NO_DEAD_SLAVE : boolean := true -- Connect to slave 0,0 on no address match (saves resources)
);
port(
clk : in std_logic; -- System clock, must be shared with all slaves and masters.
rst : in std_logic; -- Asynchronous reset
-- Master connections (crossbar is a slave)
masters_o : in wishbone_slave_in_vector; -- Connections to masters (crossbar is slave), inputs to crossbar
masters_i : out wishbone_slave_out_vector; -- Connections to masters (crossbar is slave), outputs from crossbar
-- Slave connections (crossbar is a master)
slaves_o : in wishbone_master_in_vector; -- Connections to slaves (crossbar is master), inputs to crossbar
slaves_i : out wishbone_master_out_vector; -- Connections to slaves (crossbar is master), outputs from crossbar
-- Address configuration of the slaves connected
address : in wishbone_address_vector; -- Base address for each slave connection. Must be the same size as `master_i` and `master_o`
mask : in wishbone_address_vector -- Bit mask for slave base addresses. Must be the same size as `master_i` and `master_o`
);
end wishbone_arbiter;
architecture rtl of wishbone_arbiter is
subtype slave_index is integer range 0 to slaves_i'length - 1;
subtype master_index is integer range 0 to masters_i'length - 1;
signal slave_sel : slave_index;
signal master_sel : master_index;
signal matched : std_logic;
constant SLEEPY_MASTER : wishbone_master_out := (cyc => '0', stb => '0', adr => (others => '-'), sel => (others => '-'), we => '-', dat => (others => '-'));
constant SLEEPY_SLAVE : wishbone_slave_out := (ack => '0', err => '0', rty => '0', stall => '1', dat => (others => '-'));
procedure generate_logic(
signal mo : in wishbone_slave_in_vector;
signal so : in wishbone_master_in_vector;
signal ssel : out slave_index;
signal msel : out master_index;
signal mtch : out std_logic
) is
begin
ssel <= 0;
msel <= 0;
mtch <= '0';
master_loop : for master_id in mo'range loop
if (mo(master_id).cyc and mo(master_id).stb) then
slave_loop : for slave_id in so'range loop
if (mo(master_id).adr and mask(slave_id)) = address(slave_id) then
ssel <= slave_id;
msel <= master_id;
mtch <= '1';
exit master_loop;
end if;
end loop;
end if;
end loop;
end procedure generate_logic;
procedure connect_slaves(
signal mo : in wishbone_slave_in_vector;
signal mi : out wishbone_slave_out_vector;
signal so : in wishbone_master_in_vector;
signal si : out wishbone_master_out_vector;
constant ssel : in slave_index;
constant msel : in master_index;
signal mtch : in std_logic
) is
begin
for slave_id in si'range loop
si(slave_id) <= SLEEPY_MASTER;
-- Always connect data path from selected master to *all* slave inputs
si(slave_id).dat <= mo(msel).dat;
si(slave_id).we <= mo(msel).we;
si(slave_id).sel <= mo(msel).sel;
si(slave_id).adr <= mo(msel).adr and (not mask(ssel));
if (mtch = '1' or NO_DEAD_SLAVE) and (slave_id = ssel) then -- If we are the currently active slave, then also connect these (else SLEEPY default)
si(slave_id).cyc <= mo(msel).cyc;
si(slave_id).stb <= mo(msel).stb;
end if;
end loop;
for master_id in mi'range loop
mi(master_id) <= SLEEPY_SLAVE;
-- Always connect data path from selected slave to *all* master inputs
mi(master_id).dat <= so(ssel).dat;
if (mtch = '1' or NO_DEAD_SLAVE) and (master_id = msel) then -- If we are the currently active master, also connect these (else SLEEPY default)
mi(master_id).ack <= so(ssel).ack;
mi(master_id).err <= so(ssel).err;
mi(master_id).rty <= so(ssel).rty;
mi(master_id).stall <= so(ssel).stall;
end if;
end loop;
end procedure connect_slaves;
begin
assert ASYNC = true report "Arbiter does not support ASYNC mode" severity failure;
sync : if ASYNC generate
name : process(masters_o, slaves_o, address, mask) is
begin
generate_logic(masters_o, slaves_o, slave_sel, master_sel, matched);
end process name;
else generate
sync_proc : process(rst, clk) is
begin
if rst then
slave_sel <= 0;
master_sel <= 0;
elsif rising_edge(clk) then
generate_logic(masters_o, slaves_o, slave_sel, master_sel, matched);
end if;
end process sync_proc;
end generate sync;
name : process(masters_o, slaves_o, slave_sel, master_sel, matched, mask) is
begin
connect_slaves(masters_o, masters_i, slaves_o, slaves_i, slave_sel, master_sel, matched);
end process name;
end rtl;

View File

@ -0,0 +1,51 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- 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;
package wishbone_pkg is
constant WISHBONE_ADDRESS_WIDTH : integer := 32;
constant WISHBONE_DATA_WIDTH : integer := 32;
subtype wishbone_address is std_logic_vector(WISHBONE_ADDRESS_WIDTH - 1 downto 0);
subtype wishbone_data is std_logic_vector(WISHBONE_DATA_WIDTH - 1 downto 0);
subtype wishbone_byte_select is std_logic_vector((WISHBONE_ADDRESS_WIDTH / 8) - 1 downto 0);
subtype wishbone_cycle_type is std_logic_vector(2 downto 0);
subtype wishbone_burst_type is std_logic_vector(1 downto 0);
type wishbone_master_out is record
cyc : std_logic;
stb : std_logic;
adr : wishbone_address;
sel : wishbone_byte_select;
we : std_logic;
dat : wishbone_data;
end record wishbone_master_out;
subtype wishbone_slave_in is wishbone_master_out;
type wishbone_slave_out is record
ack : std_logic;
err : std_logic;
rty : std_logic;
stall : std_logic;
dat : wishbone_data;
end record wishbone_slave_out;
subtype wishbone_master_in is wishbone_slave_out;
type wishbone_master_out_vector is array (natural range <>) of wishbone_master_out;
type wishbone_slave_out_vector is array (natural range <>) of wishbone_slave_out;
subtype wishbone_slave_in_vector is wishbone_master_out_vector;
subtype wishbone_master_in_vector is wishbone_slave_out_vector;
type wishbone_address_vector is array (natural range <>) of wishbone_address;
type wishbone_data_vector is array (natural range <>) of wishbone_data;
end wishbone_pkg;

8
fpga/hdl/header.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
files=`find bench/ design/ device_models/ generics/ -name *.vhd`
for file in $files; do
cp $file /tmp/t
cat /tmp/h /tmp/t > $file
done

1
fpga/hdl/serv Submodule

@ -0,0 +1 @@
Subproject commit 086090046c98d31787af5e657ce0d4adbd5815f2

1
fpga/hdl/trashernet Submodule

@ -0,0 +1 @@
Subproject commit 206332f3d1c1f5b704bad09b1380a8aed8b736aa

58
fpga/run.py Executable file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
from vunit import VUnit
import os
import pathlib
import subprocess
import pathlib
libraries = {
# Design files
os.path.realpath(os.path.join(__file__, "..")) : {
"bench",
"design",
"device_models",
"generics",
"serv",
"trashernet"
}
}
project_dir=pathlib.Path(__file__).parent.resolve()
libs = {}
vu = VUnit.from_argv(compile_builtins=False)
vu.add_vhdl_builtins()
for base_path in libraries:
for library_name in libraries[base_path]:
libs[library_name] = vu.add_library(library_name)
if type(libraries[base_path]) is dict: # Manually specified files
print("direct")
for filename in libraries[base_path][library_name]:
libs[library_name].add_source_files(os.path.join(base_path, filename))
else: # Auto-detect files
print(f"Importing: {library_name}")
if library_name == "trashernet": # Special case: Trashernet submodule
path = os.path.join(base_path, "hdl", library_name, library_name, "*.vhd")
libs[library_name].add_source_files(path)
elif library_name == "serv": # Special case: SERV submodule
print("Skipping SERV. It's Verilog.")
else: # Normal lib
path = os.path.join(base_path, "hdl", library_name)
if os.path.exists(path):
path=os.path.join(path, "*.vhd")
libs[library_name].add_source_files(path)
# If we don't already use our own OSVVM implementation, add the default one.
if not "osvvm" in libs:
vu.add_osvvm()
vu.add_compile_option("ghdl.a_flags", ["-frelaxed", "-fsynopsys"])
vu.add_compile_option("nvc.a_flags", ["--relaxed"])
#vu.set_sim_option("nvc.elab_flags", ["-O3"])
vu.set_sim_option("nvc.sim_flags", ["--format=fst", "--wave=wave.fst"])
vu.main()

56
fpga/run_physical.py Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
from vunit import VUnit
import os
import pathlib
import subprocess
import pathlib
libraries = {
# Design files
os.path.realpath(os.path.join(__file__, "..")) : {
"design",
"device_models",
"generics",
"serv",
"trashernet"
}
}
project_dir=pathlib.Path(__file__).parent.resolve()
libs = {}
vu = VUnit.from_argv(compile_builtins=False)
#vu.add_vhdl_builtins()
for base_path in libraries:
for library_name in libraries[base_path]:
libs[library_name] = vu.add_library(library_name)
if type(libraries[base_path]) is dict: # Manually specified files
print("direct")
for filename in libraries[base_path][library_name]:
libs[library_name].add_source_files(os.path.join(base_path, filename))
else: # Auto-detect files
print(f"Importing: {library_name}")
if library_name == "trashernet": # Special case: Trashernet submodule
path = os.path.join(base_path, "hdl", library_name, library_name, "*.vhd")
libs[library_name].add_source_files(path)
elif library_name == "serv": # Special case: SERV submodule
print("Skipping SERV. It's Verilog.")
else: # Normal lib
path = os.path.join(base_path, "hdl", library_name)
if os.path.exists(path):
path=os.path.join(path, "*.vhd")
libs[library_name].add_source_files(path)
# If we don't already use our own OSVVM implementation, add the default one.
#if not "osvvm" in libs:
# vu.add_osvvm()
vu.add_compile_option("ghdl.a_flags", ["-frelaxed", "-fsynopsys"])
vu.add_compile_option("nvc.a_flags", ["--relaxed"])
#vu.set_sim_option("nvc.elab_flags", ["-O3"])
vu.main()

View File

@ -4,13 +4,6 @@
(generator_version "8.0")
(uuid "50245fe9-16a2-46fe-8648-51ebfd485de2")
(paper "A4")
(title_block
(title "Trashernet SoC")
(date "2024-07-07")
(rev "2")
(company "notsyncing.net")
(comment 1 "https://l.notsyncing.net/tnet")
)
(lib_symbols
(symbol "Device:C"
(pin_numbers hide)
@ -2471,17 +2464,6 @@
)
(uuid "fcfe9b21-6439-4517-bc6a-f6b7dde83736")
)
(text "TODO: Power supply ramping is too fast for iCE40 devices.\nThis will (randomly) cause 100 mA of \"leakage current\".\nAlso, this violates the required power supply sequencing."
(exclude_from_sim no)
(at 72.644 66.548 0)
(effects
(font
(size 1.27 1.27)
(color 255 0 0 1)
)
)
(uuid "60bf2dd2-aa9a-460c-aecf-3ce8f474812e")
)
(symbol
(lib_id "power:+5V")
(at 80.01 38.1 0)

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,6 @@
(generator_version "8.0")
(uuid "6f17954a-662c-4681-9d8a-117fc0975850")
(paper "A4")
(title_block
(title "Trashernet SoC")
(date "2024-07-07")
(rev "2")
(company "notsyncing.net")
(comment 1 "https://l.notsyncing.net/tnet")
)
(lib_symbols
(symbol "Connector:RJ45_Amphenol_RJMG1BD3B8K1ANR"
(exclude_from_sim no)
@ -4609,12 +4602,6 @@
(color 0 0 0 0)
(uuid "261ce2bc-286e-4eec-83c1-db0778273d59")
)
(junction
(at 194.31 130.81)
(diameter 0)
(color 0 0 0 0)
(uuid "28945ddd-ab60-4b42-be94-5eab2c7d3d74")
)
(junction
(at 33.02 151.13)
(diameter 0)
@ -4687,6 +4674,12 @@
(color 0 0 0 0)
(uuid "fa6bfac9-47bb-453a-bdc4-b901db28eab8")
)
(junction
(at 194.31 102.87)
(diameter 0)
(color 0 0 0 0)
(uuid "fc96b561-289f-459e-9965-d5ceec93cb24")
)
(junction
(at 106.68 69.85)
(diameter 0)
@ -4933,7 +4926,7 @@
)
(wire
(pts
(xy 189.23 97.79) (xy 189.23 100.33)
(xy 194.31 129.54) (xy 194.31 132.08)
)
(stroke
(width 0)
@ -4971,16 +4964,6 @@
)
(uuid "1ddf6e54-a484-48cb-b03d-d61f25ed9504")
)
(wire
(pts
(xy 194.31 133.35) (xy 200.66 133.35)
)
(stroke
(width 0)
(type default)
)
(uuid "1e0e5f00-3b05-4db3-9f85-96466ce1082e")
)
(wire
(pts
(xy 106.68 77.47) (xy 121.92 77.47)
@ -5081,16 +5064,6 @@
)
(uuid "2b07da13-08e9-4826-a6f6-c3ee65fa4214")
)
(wire
(pts
(xy 189.23 97.79) (xy 194.31 97.79)
)
(stroke
(width 0)
(type default)
)
(uuid "2cdb962f-f874-46d9-bbdc-69b9e6de0b0e")
)
(wire
(pts
(xy 67.31 113.03) (xy 69.85 113.03)
@ -5103,7 +5076,7 @@
)
(wire
(pts
(xy 185.42 130.81) (xy 182.88 130.81)
(xy 185.42 102.87) (xy 182.88 102.87)
)
(stroke
(width 0)
@ -5201,6 +5174,16 @@
)
(uuid "3799491b-d693-44a3-9270-8ede3eadeead")
)
(wire
(pts
(xy 193.04 102.87) (xy 194.31 102.87)
)
(stroke
(width 0)
(type default)
)
(uuid "389dce51-7411-4289-bb71-2733ca90d344")
)
(wire
(pts
(xy 58.42 113.03) (xy 58.42 124.46)
@ -5501,16 +5484,6 @@
)
(uuid "68aeeca0-dbf0-4ac5-a6a7-33953eac8e73")
)
(wire
(pts
(xy 194.31 129.54) (xy 194.31 130.81)
)
(stroke
(width 0)
(type default)
)
(uuid "6b7b4e9f-2f7c-4fe5-80d0-5c8fb93ccc2d")
)
(wire
(pts
(xy 223.52 99.06) (xy 233.68 99.06)
@ -5521,16 +5494,6 @@
)
(uuid "6ba4c6ac-2bc9-4a50-aed9-b95f9c568999")
)
(wire
(pts
(xy 193.04 130.81) (xy 194.31 130.81)
)
(stroke
(width 0)
(type default)
)
(uuid "6c39451f-f9a6-45fb-af50-8ccccab0d0b4")
)
(polyline
(pts
(xy 219.71 93.98) (xy 223.52 93.98)
@ -5781,6 +5744,16 @@
)
(uuid "87028265-d61c-4256-afde-06187db5f018")
)
(wire
(pts
(xy 194.31 101.6) (xy 194.31 102.87)
)
(stroke
(width 0)
(type default)
)
(uuid "8c7ae453-2af4-4d62-8539-7992572d7c7a")
)
(wire
(pts
(xy 69.85 124.46) (xy 69.85 127)
@ -5921,26 +5894,6 @@
)
(uuid "9d1495ee-29e1-4ca3-94c1-a62fc85a7d15")
)
(wire
(pts
(xy 194.31 130.81) (xy 194.31 133.35)
)
(stroke
(width 0)
(type default)
)
(uuid "a05a28a0-d79b-4b56-84d4-7a1de793f622")
)
(wire
(pts
(xy 200.66 133.35) (xy 200.66 130.81)
)
(stroke
(width 0)
(type default)
)
(uuid "a4f8b6be-651d-4e43-8be5-133fb9f8df72")
)
(wire
(pts
(xy 139.7 80.01) (xy 139.7 81.28)
@ -6063,7 +6016,7 @@
)
(wire
(pts
(xy 194.31 97.79) (xy 194.31 104.14)
(xy 194.31 102.87) (xy 194.31 104.14)
)
(stroke
(width 0)
@ -6313,7 +6266,7 @@
)
(wire
(pts
(xy 182.88 130.81) (xy 182.88 133.35)
(xy 182.88 102.87) (xy 182.88 105.41)
)
(stroke
(width 0)
@ -6573,7 +6526,7 @@
)
(uuid "1da93ede-0ae8-4a68-8941-d61f3d871d57")
)
(label "CLK_50M"
(label "CLK_12M"
(at 129.54 175.26 0)
(fields_autoplaced yes)
(effects
@ -6991,7 +6944,7 @@
)
(uuid "b29a2984-e3e7-4487-a331-bb8e576b641e")
)
(label "CLK_50M"
(label "CLK_12M"
(at 88.9 185.42 180)
(fields_autoplaced yes)
(effects
@ -7311,7 +7264,7 @@
)
(symbol
(lib_id "power:GND")
(at 182.88 133.35 0)
(at 182.88 105.41 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
@ -7320,7 +7273,7 @@
(fields_autoplaced yes)
(uuid "094f2d05-80f5-414e-b1f1-87530da413db")
(property "Reference" "#PWR026"
(at 182.88 139.7 0)
(at 182.88 111.76 0)
(effects
(font
(size 1.27 1.27)
@ -7329,7 +7282,7 @@
)
)
(property "Value" "GND"
(at 182.88 138.43 0)
(at 182.88 110.49 0)
(effects
(font
(size 1.27 1.27)
@ -7337,7 +7290,7 @@
)
)
(property "Footprint" ""
(at 182.88 133.35 0)
(at 182.88 105.41 0)
(effects
(font
(size 1.27 1.27)
@ -7346,7 +7299,7 @@
)
)
(property "Datasheet" ""
(at 182.88 133.35 0)
(at 182.88 105.41 0)
(effects
(font
(size 1.27 1.27)
@ -7355,7 +7308,7 @@
)
)
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
(at 182.88 133.35 0)
(at 182.88 105.41 0)
(effects
(font
(size 1.27 1.27)
@ -7912,7 +7865,7 @@
)
(symbol
(lib_id "power:GND")
(at 189.23 100.33 0)
(at 194.31 132.08 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
@ -7921,7 +7874,7 @@
(fields_autoplaced yes)
(uuid "2378d86e-41ed-42d0-9a10-9701062fdbcf")
(property "Reference" "#PWR021"
(at 189.23 106.68 0)
(at 194.31 138.43 0)
(effects
(font
(size 1.27 1.27)
@ -7930,7 +7883,7 @@
)
)
(property "Value" "GND"
(at 189.23 105.41 0)
(at 194.31 137.16 0)
(effects
(font
(size 1.27 1.27)
@ -7938,7 +7891,7 @@
)
)
(property "Footprint" ""
(at 189.23 100.33 0)
(at 194.31 132.08 0)
(effects
(font
(size 1.27 1.27)
@ -7947,7 +7900,7 @@
)
)
(property "Datasheet" ""
(at 189.23 100.33 0)
(at 194.31 132.08 0)
(effects
(font
(size 1.27 1.27)
@ -7956,7 +7909,7 @@
)
)
(property "Description" "Power symbol creates a global label with name \"GND\" , ground"
(at 189.23 100.33 0)
(at 194.31 132.08 0)
(effects
(font
(size 1.27 1.27)
@ -8855,15 +8808,16 @@
)
(symbol
(lib_id "power:+3V3")
(at 200.66 130.81 0)
(at 194.31 101.6 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "666c8d73-fbc3-46d1-9981-ad87f3930f90")
(property "Reference" "#PWR023"
(at 200.66 134.62 0)
(at 194.31 105.41 0)
(effects
(font
(size 1.27 1.27)
@ -8872,16 +8826,15 @@
)
)
(property "Value" "+3V3"
(at 194.31 129.54 0)
(at 194.31 96.52 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" ""
(at 200.66 130.81 0)
(at 194.31 101.6 0)
(effects
(font
(size 1.27 1.27)
@ -8890,7 +8843,7 @@
)
)
(property "Datasheet" ""
(at 200.66 130.81 0)
(at 194.31 101.6 0)
(effects
(font
(size 1.27 1.27)
@ -8899,7 +8852,7 @@
)
)
(property "Description" "Power symbol creates a global label with name \"+3V3\""
(at 200.66 130.81 0)
(at 194.31 101.6 0)
(effects
(font
(size 1.27 1.27)
@ -9353,7 +9306,7 @@
)
(symbol
(lib_id "Device:C")
(at 189.23 130.81 90)
(at 189.23 102.87 90)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
@ -9361,7 +9314,7 @@
(dnp no)
(uuid "78d4edae-aade-4399-996c-c47ea7795f31")
(property "Reference" "C12"
(at 189.23 136.398 90)
(at 189.23 97.028 90)
(effects
(font
(size 1.27 1.27)
@ -9369,7 +9322,7 @@
)
)
(property "Value" "100n"
(at 189.23 134.366 90)
(at 189.23 99.314 90)
(effects
(font
(size 1.27 1.27)
@ -9377,7 +9330,7 @@
)
)
(property "Footprint" "Capacitor_SMD:C_0603_1608Metric"
(at 193.04 129.8448 0)
(at 193.04 101.9048 0)
(effects
(font
(size 1.27 1.27)
@ -9386,7 +9339,7 @@
)
)
(property "Datasheet" "~"
(at 189.23 130.81 0)
(at 189.23 102.87 0)
(effects
(font
(size 1.27 1.27)
@ -9395,7 +9348,7 @@
)
)
(property "Description" "Unpolarized capacitor"
(at 189.23 130.81 0)
(at 189.23 102.87 0)
(effects
(font
(size 1.27 1.27)
@ -11652,7 +11605,7 @@
)
)
)
(property "Value" "50MHz"
(property "Value" "12MHz"
(at 72.898 179.07 0)
(effects
(font

View File

@ -4,13 +4,6 @@
(generator_version "8.0")
(uuid "d7368bdd-94df-4bcb-8823-60f77936df1c")
(paper "A4")
(title_block
(title "Trashernet SoC")
(date "2024-07-07")
(rev "2")
(company "notsyncing.net")
(comment 1 "https://l.notsyncing.net/tnet")
)
(lib_symbols
(symbol "Connector:USB_B_Micro"
(pin_names

4
sw/bootrom/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.bin
*.elf
*.hex
*.vhex

45
sw/bootrom/boot.ld Normal file
View File

@ -0,0 +1,45 @@
__heap_size = 0x80;
__stack_size = 0x80;
MEMORY
{
ROM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x100
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x400
}
SECTIONS
{
.text :
{
*(.boot)
*(.text)
*(.text)
*(.rodata*)
} > ROM
.data :
{
*(.sbss)
*(.data)
*(.bss)
*(.rela*)
*(COMMON)
} > RAM
.heap :
{
. = ALIGN(4);
PROVIDE ( end = . );
_sheap = .;
. = . + __heap_size;
. = ALIGN(4);
_eheap = .;
} >RAM
.stack :
{
. = ALIGN(4);
_estack = .;
. = . + __stack_size;
. = ALIGN(4);
_sstack = .;
} >RAM
}

8
sw/bootrom/build.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
riscv64-elf-gcc -mabi=ilp32 -march=rv32i -nostdlib -nostartfiles -ffreestanding -T boot.ld -Os -o bootrom.elf main.c
#riscv64-elf-objdump -D bootrom.elf
riscv64-elf-objcopy -O binary bootrom.elf bootrom.bin
od --endian=little -vtx4 -An -w4 bootrom.bin | tr -d ' ' > bootrom.vhex
r2 -A -c 'pdf @ sym._start' -q bootrom.elf

73
sw/bootrom/main.c Normal file
View File

@ -0,0 +1,73 @@
#include <inttypes.h>
#define UART0_BASE 0x81000000
#define UART0_SR (*((volatile uint32_t *) (UART0_BASE + 0x00)))
#define UART0_DR (*((volatile uint32_t *) (UART0_BASE + 0x04)))
#define UART0_SR_RX_DATA_EMPTY (1 << 0)
#define UART0_SR_TX_FULL (1 << 1)
/*
// Set up stack pointer
asm("_start:\
xor sp, sp, sp;\
lui sp, 0x80100;\
j main;\
");
*/
__attribute__((noreturn)) void _start() {
uint8_t state = 0;
uint8_t c;
uint8_t opcode;
uint8_t *ptr;
uint32_t length;
uint32_t sr;
/*while (1) {
while (UART0_SR & UART0_SR_RX_DATA_EMPTY);
c = UART0_DR;
while (UART0_SR & UART0_SR_TX_FULL);
UART0_DR = c;
}*/
while (1) {
while (UART0_SR & UART0_SR_RX_DATA_EMPTY);
c = (uint8_t)UART0_DR;
sr = (sr << 8) | c;
state++;
switch (state) {
case 1: // Opcode
opcode = c;
if (c == 0) // NOP
state = 0;
break;
case 5: // Address
ptr = (uint8_t*)sr;
if (opcode == 3) { // Jump
//((void (*)()) ptr)();
asm("jalr %0" : : "r"(ptr)); // Not sure why, but the jump above causes GCC to save variables to the stack at the beginning of this function
__builtin_unreachable();
}
break;
case 9: // Data
length = sr;
if (opcode == 2) { // Read
for (; length > 0; length--) {
while (UART0_SR & UART0_SR_TX_FULL);
UART0_DR = *(ptr++);
}
} else { // Write
for (; length > 0; length--) {
while (UART0_SR & UART0_SR_RX_DATA_EMPTY);
*(ptr++) = UART0_DR;
}
}
state = 0;
break;
default:
break;
}
}
__builtin_unreachable();
}

115
tools/flash.py Executable file
View File

@ -0,0 +1,115 @@
#!/usr/bin/env python
# -------------------------------------------------------------------------- --
# TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
# -------------------------------------------------------------------------- --
# Interface program for the bootrom. Basically a serial-to-wishbone bridge.
# -------------------------------------------------------------------------- --
# Author : Markus Koch <markus@notsyncing.net>
# Contributors : None
# License : Mozilla Public License (MPL) Version 2
# -------------------------------------------------------------------------- --
import serial
import sys
from ast import literal_eval
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
ser = serial.Serial('/dev/ttyUSB0', 250000, timeout=2)
def sread(address, length, quiet = False):
if not quiet:
eprint(f'Reading {length} bytes from 0x{address:08x}...')
ser.write(b'\x02' + address.to_bytes(4, byteorder='big') + length.to_bytes(4, byteorder='big'))
data = bytes()
while length > 0:
maxpkg = min(length, 128)
rd = ser.read(maxpkg)
if len(rd) == 0:
eprint("ERROR: Receive timeout")
return None
data += rd
length -= len(rd)
return data
def swrite(address, data, quiet = False):
if not quiet:
eprint('Writing {} bytes to 0x{:08x}...'.format(len(data), address))
ser.write(b'\x01' + address.to_bytes(4, byteorder='big') + len(data).to_bytes(4, byteorder='big') + data)
return 0
def sjump(address, quiet = False):
if not quiet:
eprint(f'Jumping to 0x{address:08x}...')
ser.write(b'\x03' + address.to_bytes(4, byteorder='big'))
return 0
def get_diff(a, b):
diff = 0
for i in range(0, len(a)):
if a[i] != b[i]:
print('[0x{:08x}] 0x{:02x} => 0x{:02x}'.format(i, a[i], b[i]))
diff += 1
return diff
def probe():
r = sread(0, 4, quiet = True);
if r is None:
eprint("ERROR: Probe failed! Check that the device is in bootloader mode.")
return 1
return 0
def program(filename, addr=0, check=True):
f = open(filename, "rb")
d = bytes(f.read())
swrite(addr, d)
if check:
r = sread(addr, len(d))
if r is None:
eprint("Programming failed")
return 1
diff_cnt = get_diff(d, r)
if diff_cnt == 0:
eprint("Programming OK.")
return 0
else:
print(f"Programming failed, {diff_cnt} bytes differ.")
return 2
else:
return 0
def monitor():
print("Monitoring serial, press Ctrl+C to stop.")
while 1:
rd = ser.read(1)
if len(rd) > 0:
sys.stdout.buffer.write(bytes(rd))
sys.stdout.buffer.flush()
if len(sys.argv) < 3:
eprint("usage: " + sys.argv[0] + " [write|dump|jump] [address] [file]")
exit()
command = sys.argv[1]
addr = literal_eval(sys.argv[2])
if probe():
exit(-1)
ret = 0
if command == "write":
file = sys.argv[3]
ret = program(file, addr)
elif command == "dump":
length = literal_eval(sys.argv[3])
sys.stdout.buffer.write(sread(addr, length))
elif command == "jump":
ret = sjump(addr)
monitor()
else:
ret = -2
eprint("Unknown command")
exit(ret)