From 8fa16dc3aac1dfe6d1d676946f720631b5c2a9d2 Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Wed, 12 Jun 2024 20:03:04 +0200 Subject: [PATCH] fpga: Implement basic SERV SoC --- .gitmodules | 6 + fpga/.gitignore | 2 + fpga/build.sh | 28 ++ fpga/constraints.pcf | 44 ++++ fpga/hdl/bench/bench_aps6404l_controller.vhd | 117 +++++++++ fpga/hdl/bench/bench_fifo_block.vhd | 121 +++++++++ fpga/hdl/bench/bench_top_manual.vhd | 221 ++++++++++++++++ fpga/hdl/design/aps6404l_controller.vhd | 256 +++++++++++++++++++ fpga/hdl/design/aps6404l_wb.vhd | 136 ++++++++++ fpga/hdl/design/pll0.vhd | 25 ++ fpga/hdl/design/serv_top_vhdl.vhd | 91 +++++++ fpga/hdl/design/servant_gpio_vhdl.vhd | 58 +++++ fpga/hdl/design/servant_ram.vhd | 90 +++++++ fpga/hdl/design/servant_timer_vhdl.vhd | 65 +++++ fpga/hdl/design/top.vhd | 247 ++++++++++++++++++ fpga/hdl/design/trashernet_phy_wb.vhd | 97 +++++++ fpga/hdl/design/uart_wb.vhd | 123 +++++++++ fpga/hdl/device_models/aps6404l.vhd | 178 +++++++++++++ fpga/hdl/generics/fifo_block.vhd | 125 +++++++++ fpga/hdl/generics/synchronizer.vhd | 55 ++++ fpga/hdl/generics/uart_rx.vhd | 99 +++++++ fpga/hdl/generics/uart_tx.vhd | 96 +++++++ fpga/hdl/generics/wishbone_arbiter.vhd | 136 ++++++++++ fpga/hdl/generics/wishbone_pkg.vhd | 51 ++++ fpga/hdl/header.sh | 8 + fpga/hdl/serv | 1 + fpga/hdl/trashernet | 1 + fpga/run.py | 57 +++++ 28 files changed, 2534 insertions(+) create mode 100644 .gitmodules create mode 100644 fpga/.gitignore create mode 100755 fpga/build.sh create mode 100644 fpga/constraints.pcf create mode 100644 fpga/hdl/bench/bench_aps6404l_controller.vhd create mode 100644 fpga/hdl/bench/bench_fifo_block.vhd create mode 100644 fpga/hdl/bench/bench_top_manual.vhd create mode 100644 fpga/hdl/design/aps6404l_controller.vhd create mode 100644 fpga/hdl/design/aps6404l_wb.vhd create mode 100644 fpga/hdl/design/pll0.vhd create mode 100644 fpga/hdl/design/serv_top_vhdl.vhd create mode 100644 fpga/hdl/design/servant_gpio_vhdl.vhd create mode 100644 fpga/hdl/design/servant_ram.vhd create mode 100644 fpga/hdl/design/servant_timer_vhdl.vhd create mode 100644 fpga/hdl/design/top.vhd create mode 100644 fpga/hdl/design/trashernet_phy_wb.vhd create mode 100644 fpga/hdl/design/uart_wb.vhd create mode 100644 fpga/hdl/device_models/aps6404l.vhd create mode 100644 fpga/hdl/generics/fifo_block.vhd create mode 100644 fpga/hdl/generics/synchronizer.vhd create mode 100644 fpga/hdl/generics/uart_rx.vhd create mode 100644 fpga/hdl/generics/uart_tx.vhd create mode 100644 fpga/hdl/generics/wishbone_arbiter.vhd create mode 100644 fpga/hdl/generics/wishbone_pkg.vhd create mode 100755 fpga/hdl/header.sh create mode 160000 fpga/hdl/serv create mode 160000 fpga/hdl/trashernet create mode 100755 fpga/run.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ab92ef6 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/fpga/.gitignore b/fpga/.gitignore new file mode 100644 index 0000000..b6df2ed --- /dev/null +++ b/fpga/.gitignore @@ -0,0 +1,2 @@ +build/** +*.bak diff --git a/fpga/build.sh b/fpga/build.sh new file mode 100755 index 0000000..86ee281 --- /dev/null +++ b/fpga/build.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +mkdir -p build +cd build + +set -e + +../run.py --compile + +BASEDIR=".." +DEVICE=--u4k # --up5k, --u4k +PACKAGE=sg48 + +# Collect pre-analyzed VHDL sources +GHDLINCDIRS=`find ./vunit_out/ghdl/libraries -maxdepth 1 -mindepth 1 -type d | sed "s/^/-P/" | tr '\n' ' '` + +# Collect Verilog sources +SERV="$BASEDIR/hdl/serv" +VLOGS=`echo ../hdl/serv/rtl/*.v` +VLOGS="$VLOGS $SERV/servant/servant_ram.v $SERV/servant/servant_timer.v" + +# Synthesize and PnR +# -device +yosys -m ghdl -p "read_verilog $VLOGS; ghdl --std=08 $GHDLINCDIRS design.top; synth_ice40 -abc9 -device u -top top -json netlist.json" +nextpnr-ice40 $DEVICE --package $PACKAGE --freq 12 --asc netlist.asc --report report.json --detailed-timing-report --json netlist.json --pcf ../constraints.pcf + +# Generate bitstream +icepack netlist.asc bitstream.bin diff --git a/fpga/constraints.pcf b/fpga/constraints.pcf new file mode 100644 index 0000000..1353d28 --- /dev/null +++ b/fpga/constraints.pcf @@ -0,0 +1,44 @@ +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_12m 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 40 +set_io eth_led_orange 41 + +set_io led_user 34 diff --git a/fpga/hdl/bench/bench_aps6404l_controller.vhd b/fpga/hdl/bench/bench_aps6404l_controller.vhd new file mode 100644 index 0000000..2a0c73c --- /dev/null +++ b/fpga/hdl/bench/bench_aps6404l_controller.vhd @@ -0,0 +1,117 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/bench/bench_fifo_block.vhd b/fpga/hdl/bench/bench_fifo_block.vhd new file mode 100644 index 0000000..eea0b38 --- /dev/null +++ b/fpga/hdl/bench/bench_fifo_block.vhd @@ -0,0 +1,121 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/bench/bench_top_manual.vhd b/fpga/hdl/bench/bench_top_manual.vhd new file mode 100644 index 0000000..17a7b2e --- /dev/null +++ b/fpga/hdl/bench/bench_top_manual.vhd @@ -0,0 +1,221 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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_12m => 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 diff --git a/fpga/hdl/design/aps6404l_controller.vhd b/fpga/hdl/design/aps6404l_controller.vhd new file mode 100644 index 0000000..c19fbfd --- /dev/null +++ b/fpga/hdl/design/aps6404l_controller.vhd @@ -0,0 +1,256 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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 + 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; + + 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 <= QPI_SET_BOUNDARY 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; + diff --git a/fpga/hdl/design/aps6404l_wb.vhd b/fpga/hdl/design/aps6404l_wb.vhd new file mode 100644 index 0000000..018c69f --- /dev/null +++ b/fpga/hdl/design/aps6404l_wb.vhd @@ -0,0 +1,136 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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 aps6404l_wb is + 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) + + -- 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 + signal apsc_addr : std_logic_vector(23 downto 0); + signal apsc_data_in : std_logic_vector(7 downto 0); + signal apsc_data_in_next : std_logic; + signal apsc_data_out : std_logic_vector(7 downto 0); + signal apsc_data_out_valid : std_logic; + signal apsc_write_en : std_logic; + signal apsc_read_en : std_logic; + + signal bit_cnt : integer range 0 to 4; + signal data : std_logic_vector(wb_i.dat'range); + +begin + aps6404l_controller_inst : entity work.aps6404l_controller + port map( + clk => clk, + rst => rst, + addr => apsc_addr, + data_in => apsc_data_in, + data_in_next => apsc_data_in_next, + data_out => apsc_data_out, + data_out_valid => apsc_data_out_valid, + write_en => apsc_write_en, + read_en => apsc_read_en, + psram_ce_n => psram_ce_n, + psram_sclk => psram_sclk, + psram_sio => psram_sio + ); + + 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; + begin + if rst then + wb_o.ack <= '0'; + bit_cnt <= 0; + + elsif rising_edge(clk) then + wb_o.ack <= '0'; + + if bit_cnt = 0 then + apsc_read_en <= '0'; + apsc_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; + + apsc_addr <= wb_i.adr(apsc_addr'length - 1 downto 2) & std_logic_vector(to_unsigned(start_addr, 2)); + data <= wb_i.dat; + + if not wb_o.ack and wb_i.stb and wb_i.cyc then + bit_cnt <= 4; + end if; + else + success := '0'; + + if bit_cnt >= (4 - stop_addr) and bit_cnt <= (4 - start_addr) then + apsc_read_en <= not wb_i.we; + apsc_write_en <= wb_i.we; + else + apsc_read_en <= '0'; + apsc_write_en <= '0'; + success := '1'; + end if; + + if wb_i.we and apsc_data_in_next then + success := '1'; + elsif not wb_i.we and apsc_data_out_valid then + success := '1'; + end if; + + if success then + bit_cnt <= bit_cnt - 1; + wb_o.dat <= apsc_data_out & wb_o.dat(wb_o.dat'high downto 8); + data <= x"--" & data(data'high downto 8); + if bit_cnt = 1 then + wb_o.ack <= '1'; + end if; + end if; + end if; + + end if; + end process main; + apsc_data_in <= data(apsc_data_in'range); + + wb_o.err <= '0'; + wb_o.rty <= '0'; + wb_o.stall <= not wb_o.ack; + +end architecture rtl; diff --git a/fpga/hdl/design/pll0.vhd b/fpga/hdl/design/pll0.vhd new file mode 100644 index 0000000..dba159e --- /dev/null +++ b/fpga/hdl/design/pll0.vhd @@ -0,0 +1,25 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- Contributors : None +-- License : Mozilla Public License (MPL) Version 2 +-- -------------------------------------------------------------------------- -- + +library IEEE; +use IEEE.std_logic_1164.all; + +entity pll0 is + port( + CLKI : in std_logic; + CLKOP : out std_logic; + LOCK : out std_logic); +end pll0; + +architecture Structure of pll0 is +begin + CLKOP <= CLKI; + LOCK <= '1'; +end Structure; diff --git a/fpga/hdl/design/serv_top_vhdl.vhd b/fpga/hdl/design/serv_top_vhdl.vhd new file mode 100644 index 0000000..2d1563b --- /dev/null +++ b/fpga/hdl/design/serv_top_vhdl.vhd @@ -0,0 +1,91 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/design/servant_gpio_vhdl.vhd b/fpga/hdl/design/servant_gpio_vhdl.vhd new file mode 100644 index 0000000..f837f84 --- /dev/null +++ b/fpga/hdl/design/servant_gpio_vhdl.vhd @@ -0,0 +1,58 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/design/servant_ram.vhd b/fpga/hdl/design/servant_ram.vhd new file mode 100644 index 0000000..f7aca9c --- /dev/null +++ b/fpga/hdl/design/servant_ram.vhd @@ -0,0 +1,90 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/design/servant_timer_vhdl.vhd b/fpga/hdl/design/servant_timer_vhdl.vhd new file mode 100644 index 0000000..dc662ec --- /dev/null +++ b/fpga/hdl/design/servant_timer_vhdl.vhd @@ -0,0 +1,65 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/design/top.vhd b/fpga/hdl/design/top.vhd new file mode 100644 index 0000000..515569a --- /dev/null +++ b/fpga/hdl/design/top.vhd @@ -0,0 +1,247 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; + +library trashernet; +use trashernet.all; + +entity top is + generic( + -- System configuration + F_CLK : integer := 12000000; + F_CLK_PHY : integer := 48000000; + UART_BAUD : integer := 9600 + ); + port( + clk_12m : 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 : out std_logic; + eth_led_orange : 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 + component pll0 + port( + CLKI : in std_logic; + CLKOP : out std_logic; + LOCK : out std_logic + ); + end component pll0; + + -- System + signal pll_locked : std_logic; + signal clk : 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; + + -- System Timer + signal irq_timer : std_logic; + + -- Wishbone interconnect (master) + signal wb_masters_o : wishbone_slave_in_vector(1 downto 0); + signal wb_masters_i : wishbone_slave_out_vector(wb_masters_o'range); + alias wbi_o is wb_masters_o(0); + alias wbi_i is wb_masters_i(0); + alias wbd_o is wb_masters_o(1); + alias wbd_i is wb_masters_i(1); + + -- Wishbone interconnect (slave) + signal wb_slaves_o : wishbone_master_in_vector(4 downto 0); + signal wb_slaves_i : wishbone_master_out_vector(wb_slaves_o'range); + alias wb_rom_o is wb_slaves_o(0); + alias wb_rom_i is wb_slaves_i(0); + alias wb_ram_o is wb_slaves_o(1); + alias wb_ram_i is wb_slaves_i(1); + alias wb_timer_o is wb_slaves_o(2); + alias wb_timer_i is wb_slaves_i(2); + alias wb_uart_o is wb_slaves_o(3); + alias wb_uart_i is wb_slaves_i(3); + alias wb_eth_o is wb_slaves_o(4); + alias wb_eth_i is wb_slaves_i(4); + + -- Slave address map + constant wishbone_addresses : wishbone_address_vector := ( + 0 => x"00000000", -- Boot ROM + 1 => x"40000000", -- RAM + 2 => x"80000000", -- Timer + 3 => x"81000000", -- UART + 4 => x"82000000" -- Eth + ); + constant wishbone_masks : wishbone_address_vector := ( + 0 => x"FF000000", -- Boot ROM: 256b + 1 => x"FF000000", -- RAM: Max 16M + 2 => x"FF000000", -- Timer + 3 => x"FF000000", -- UART + 4 => x"FF000000" -- Eth + ); + + constant IN_SIMULATION : boolean := false -- + -- pragma translate_off + or true -- + -- pragma translate_on + ; + +begin + 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 : pll0 + port map( + CLKI => clk_12m, + CLKOP => clk, + LOCK => pll_locked + ); + end generate clockgen; + + por : process(clk) is + variable reset_done : std_logic := '0'; + begin + if rising_edge(clk) then + rst <= not reset_done; + clr <= not reset_done; + reset_done := '1'; + 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 => wb_rom_o, + wb_i => wb_rom_i + ); + + aps6404l_wb_inst : entity work.aps6404l_wb + port map( + clk => clk, + rst => rst, + 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 => 9600 + ) + 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, + rst => rst, + wb_o => wb_eth_o, + wb_i => wb_eth_i, + rx_p => eth_rx_p, + tx_p => eth_tx_p_i, + tx_n => eth_tx_n_i + ); + eth_tx_p <= (others => eth_tx_p_i); + eth_tx_n <= (others => eth_tx_n_i); + + wishbone_crossbar_inst : entity generics.wishbone_arbiter + port map( + clk => clk, + rst => rst, + masters_o => wb_masters_o, + masters_i => wb_masters_i, + slaves_o => wb_slaves_o, + slaves_i => wb_slaves_i, + address => wishbone_addresses, + mask => wishbone_masks + ); + + eth_led_green <= '0'; + eth_led_orange <= '1'; + led_user <= '1'; +end architecture rtl; diff --git a/fpga/hdl/design/trashernet_phy_wb.vhd b/fpga/hdl/design/trashernet_phy_wb.vhd new file mode 100644 index 0000000..0cbcb9a --- /dev/null +++ b/fpga/hdl/design/trashernet_phy_wb.vhd @@ -0,0 +1,97 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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.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 + ); +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 wb_adr : unsigned(2 downto 0); + + signal status_register : std_logic_vector(31 downto 0); +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 + ); + + wb_adr <= unsigned(wb_i.adr(wb_adr'range)); + wbif : process(clk, rst) is + begin + if rst then + wb_o.ack <= '0'; + + elsif rising_edge(clk) then + wb_o.ack <= '0'; + + if (wb_i.cyc and wb_i.stb) then + wb_o.ack <= '1'; + if wb_adr = 0 then + wb_o.dat <= status_register; + else + wb_o.dat <= (others => '0'); + wb_o.dat(7 downto 0) <= phy_out.rx_data; -- TODO: data + end if; + end if; + end if; + end process wbif; + + wb_o.err <= '0'; + wb_o.rty <= '1'; + wb_o.stall <= '0'; + + phy_in.tx_data_en <= '0'; + phy_in.tx_data <= (others => '0'); + + status_register <= (x"0000000" & "000" & phy_out.carrier_detect); +end architecture RTL; diff --git a/fpga/hdl/design/uart_wb.vhd b/fpga/hdl/design/uart_wb.vhd new file mode 100644 index 0000000..870e6e1 --- /dev/null +++ b/fpga/hdl/design/uart_wb.vhd @@ -0,0 +1,123 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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_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; + in_data_available <= '0'; + end if; + end if; + end if; + + if in_data_valid then + in_data_available <= '1'; + end if; + end if; + end process wb_if; + + wb_o.err <= '0'; + wb_o.stall <= '0'; + wb_o.rty <= '0'; + +end architecture rtl; diff --git a/fpga/hdl/device_models/aps6404l.vhd b/fpga/hdl/device_models/aps6404l.vhd new file mode 100644 index 0000000..3a4d560 --- /dev/null +++ b/fpga/hdl/device_models/aps6404l.vhd @@ -0,0 +1,178 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/generics/fifo_block.vhd b/fpga/hdl/generics/fifo_block.vhd new file mode 100644 index 0000000..a54e52f --- /dev/null +++ b/fpga/hdl/generics/fifo_block.vhd @@ -0,0 +1,125 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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 -- 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'; + + 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; + 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; diff --git a/fpga/hdl/generics/synchronizer.vhd b/fpga/hdl/generics/synchronizer.vhd new file mode 100644 index 0000000..4776665 --- /dev/null +++ b/fpga/hdl/generics/synchronizer.vhd @@ -0,0 +1,55 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/generics/uart_rx.vhd b/fpga/hdl/generics/uart_rx.vhd new file mode 100644 index 0000000..2e9a207 --- /dev/null +++ b/fpga/hdl/generics/uart_rx.vhd @@ -0,0 +1,99 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/generics/uart_tx.vhd b/fpga/hdl/generics/uart_tx.vhd new file mode 100644 index 0000000..aab909a --- /dev/null +++ b/fpga/hdl/generics/uart_tx.vhd @@ -0,0 +1,96 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/generics/wishbone_arbiter.vhd b/fpga/hdl/generics/wishbone_arbiter.vhd new file mode 100644 index 0000000..0a66e6c --- /dev/null +++ b/fpga/hdl/generics/wishbone_arbiter.vhd @@ -0,0 +1,136 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/generics/wishbone_pkg.vhd b/fpga/hdl/generics/wishbone_pkg.vhd new file mode 100644 index 0000000..709d561 --- /dev/null +++ b/fpga/hdl/generics/wishbone_pkg.vhd @@ -0,0 +1,51 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- TODO +-- -------------------------------------------------------------------------- -- +-- Author : Markus Koch +-- 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; diff --git a/fpga/hdl/header.sh b/fpga/hdl/header.sh new file mode 100755 index 0000000..355e4b7 --- /dev/null +++ b/fpga/hdl/header.sh @@ -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 diff --git a/fpga/hdl/serv b/fpga/hdl/serv new file mode 160000 index 0000000..0860900 --- /dev/null +++ b/fpga/hdl/serv @@ -0,0 +1 @@ +Subproject commit 086090046c98d31787af5e657ce0d4adbd5815f2 diff --git a/fpga/hdl/trashernet b/fpga/hdl/trashernet new file mode 160000 index 0000000..4bce111 --- /dev/null +++ b/fpga/hdl/trashernet @@ -0,0 +1 @@ +Subproject commit 4bce111d69883fc52a6446dd3c4354d6dfe541d3 diff --git a/fpga/run.py b/fpga/run.py new file mode 100755 index 0000000..63e335d --- /dev/null +++ b/fpga/run.py @@ -0,0 +1,57 @@ +#!/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() +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()