diff --git a/demo/lattice_brevia2/.gitignore b/demo/lattice_brevia2/.gitignore
new file mode 100644
index 0000000..0e1701e
--- /dev/null
+++ b/demo/lattice_brevia2/.gitignore
@@ -0,0 +1,7 @@
+impl1/
+*.ccl
+.recovery
+.run_manager.ini
+.setting.ini
+.spread_sheet.ini
+.spreadsheet_view.ini
diff --git a/demo/lattice_brevia2/ip/pll0.vhd b/demo/lattice_brevia2/ip/pll0.vhd
new file mode 100644
index 0000000..f06548c
--- /dev/null
+++ b/demo/lattice_brevia2/ip/pll0.vhd
@@ -0,0 +1,112 @@
+-- VHDL netlist generated by SCUBA Diamond (64-bit) 3.12.0.240.2
+-- Module Version: 5.7
+--/usr/local/diamond/3.12/ispfpga/bin/lin64/scuba -w -n pll0 -lang vhdl -synth lse -arch mg5a00 -type pll -fin 50 -phase_cntl STATIC -fclkop 140 -fclkop_tol 0.0 -fb_mode CLOCKTREE -noclkos -noclkok -norst -noclkok2
+
+-- Sat Jan 22 20:39:11 2022
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+-- synopsys translate_off
+library xp2;
+use xp2.components.all;
+-- synopsys translate_on
+
+entity pll0 is
+ port(
+ CLK : in std_logic;
+ CLKOP : out std_logic;
+ LOCK : out std_logic);
+end pll0;
+
+architecture Structure of pll0 is
+
+ -- internal signal declarations
+ signal CLKOP_t : std_logic;
+ signal scuba_vlo : std_logic;
+
+ -- local component declarations
+ component VLO
+ port(Z : out std_logic);
+ end component;
+ component EPLLD1
+ -- synopsys translate_off
+ generic(CLKOK_BYPASS : in String; CLKOS_BYPASS : in String;
+ CLKOP_BYPASS : in String; DUTY : in Integer;
+ PHASEADJ : in String; PHASE_CNTL : in String;
+ CLKOK_DIV : in Integer; CLKFB_DIV : in Integer;
+ CLKOP_DIV : in Integer; CLKI_DIV : in Integer);
+ -- synopsys translate_on
+ port(CLKI : in std_logic; CLKFB : in std_logic; RST : in std_logic;
+ RSTK : in std_logic; DPAMODE : in std_logic; DRPAI3 : in std_logic;
+ DRPAI2 : in std_logic; DRPAI1 : in std_logic; DRPAI0 : in std_logic;
+ DFPAI3 : in std_logic; DFPAI2 : in std_logic; DFPAI1 : in std_logic;
+ DFPAI0 : in std_logic; PWD : in std_logic; CLKOP : out std_logic;
+ CLKOS : out std_logic; CLKOK : out std_logic; LOCK : out std_logic;
+ CLKINTFB : out std_logic);
+ end component;
+ attribute CLKOK_BYPASS : string;
+ attribute CLKOS_BYPASS : string;
+ attribute FREQUENCY_PIN_CLKOP : string;
+ attribute CLKOP_BYPASS : string;
+ attribute PHASE_CNTL : string;
+ attribute DUTY : string;
+ attribute PHASEADJ : string;
+ attribute FREQUENCY_PIN_CLKI : string;
+ attribute CLKOK_DIV : string;
+ attribute CLKOP_DIV : string;
+ attribute CLKFB_DIV : string;
+ attribute CLKI_DIV : string;
+ attribute FIN : string;
+ attribute CLKOK_BYPASS of PLLInst_0 : label is "DISABLED";
+ attribute CLKOS_BYPASS of PLLInst_0 : label is "DISABLED";
+ attribute FREQUENCY_PIN_CLKOP of PLLInst_0 : label is "140.000000";
+ attribute CLKOP_BYPASS of PLLInst_0 : label is "DISABLED";
+ attribute PHASE_CNTL of PLLInst_0 : label is "STATIC";
+ attribute DUTY of PLLInst_0 : label is "8";
+ attribute PHASEADJ of PLLInst_0 : label is "0.0";
+ attribute FREQUENCY_PIN_CLKI of PLLInst_0 : label is "50.000000";
+ attribute CLKOK_DIV of PLLInst_0 : label is "2";
+ attribute CLKOP_DIV of PLLInst_0 : label is "4";
+ attribute CLKFB_DIV of PLLInst_0 : label is "14";
+ attribute CLKI_DIV of PLLInst_0 : label is "5";
+ attribute FIN of PLLInst_0 : label is "50.000000";
+ attribute syn_keep : boolean;
+ attribute NGD_DRC_MASK : integer;
+ attribute NGD_DRC_MASK of Structure : architecture is 1;
+
+begin
+ -- component instantiation statements
+ scuba_vlo_inst : VLO
+ port map(Z => scuba_vlo);
+
+ PLLInst_0 : EPLLD1
+ -- synopsys translate_off
+ generic map(CLKOK_BYPASS => "DISABLED", CLKOS_BYPASS => "DISABLED",
+ CLKOP_BYPASS => "DISABLED", PHASE_CNTL => "STATIC", DUTY => 8,
+ PHASEADJ => "0.0", CLKOK_DIV => 2, CLKOP_DIV => 4, CLKFB_DIV => 14,
+ CLKI_DIV => 5)
+ -- synopsys translate_on
+ port map(CLKI => CLK, CLKFB => CLKOP_t, RST => scuba_vlo,
+ RSTK => scuba_vlo, DPAMODE => scuba_vlo, DRPAI3 => scuba_vlo,
+ DRPAI2 => scuba_vlo, DRPAI1 => scuba_vlo, DRPAI0 => scuba_vlo,
+ DFPAI3 => scuba_vlo, DFPAI2 => scuba_vlo, DFPAI1 => scuba_vlo,
+ DFPAI0 => scuba_vlo, PWD => scuba_vlo, CLKOP => CLKOP_t,
+ CLKOS => open, CLKOK => open, LOCK => LOCK, CLKINTFB => open);
+
+ CLKOP <= CLKOP_t;
+end Structure;
+
+-- synopsys translate_off
+library xp2;
+configuration Structure_CON of pll0 is
+ for Structure
+ for all : VLO
+ use entity xp2.VLO(V);
+ end for;
+ for all : EPLLD1
+ use entity xp2.EPLLD1(V);
+ end for;
+ end for;
+end Structure_CON;
+
+-- synopsys translate_on
diff --git a/demo/lattice_brevia2/mac_test/mac_test.ldf b/demo/lattice_brevia2/mac_test/mac_test.ldf
new file mode 100644
index 0000000..d030f2e
--- /dev/null
+++ b/demo/lattice_brevia2/mac_test/mac_test.ldf
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/lattice_brevia2/mac_test/mac_test.lpf b/demo/lattice_brevia2/mac_test/mac_test.lpf
new file mode 100644
index 0000000..d7a7fb9
--- /dev/null
+++ b/demo/lattice_brevia2/mac_test/mac_test.lpf
@@ -0,0 +1,47 @@
+BLOCK RESETPATHS ;
+BLOCK ASYNCPATHS ;
+LOCATE COMP "clk" SITE "21" ;
+LOCATE COMP "rst_n" SITE "19" ;
+LOCATE COMP "rx_p" SITE "94" ;
+LOCATE COMP "led_n[0]" SITE "37" ;
+LOCATE COMP "led_n[1]" SITE "38" ;
+LOCATE COMP "led_n[2]" SITE "39" ;
+LOCATE COMP "led_n[3]" SITE "40" ;
+LOCATE COMP "led_n[4]" SITE "43" ;
+LOCATE COMP "led_n[5]" SITE "44" ;
+LOCATE COMP "led_n[6]" SITE "45" ;
+LOCATE COMP "led_n[7]" SITE "46" ;
+IOBUF PORT "rx_p" IO_TYPE=LVPECL33 ;
+IOBUF PORT "clk" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "rst_n" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[0]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[1]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[2]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[3]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[4]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[5]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[6]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "debug_data[7]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[0]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[1]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[2]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[3]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[4]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[5]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[6]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "led_n[7]" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "tx_n" IO_TYPE=LVCMOS33 ;
+IOBUF PORT "tx_p" IO_TYPE=LVCMOS33 ;
+LOCATE COMP "tx_n" SITE "114" ;
+LOCATE COMP "tx_p" SITE "116" ;
+LOCATE COMP "button_n[0]" SITE "54" ;
+LOCATE COMP "debug_data[0]" SITE "73" ;
+LOCATE COMP "debug_data[1]" SITE "74" ;
+LOCATE COMP "debug_data[2]" SITE "77" ;
+LOCATE COMP "debug_data[3]" SITE "87" ;
+LOCATE COMP "debug_data[4]" SITE "88" ;
+LOCATE COMP "debug_data[5]" SITE "89" ;
+LOCATE COMP "debug_data[6]" SITE "90" ;
+LOCATE COMP "debug_data[7]" SITE "91" ;
+BLOCK PATH FROM CLKNET "clk_c" TO CLKNET "clk_phy" ;
+BLOCK PATH FROM CLKNET "clk_phy" TO CLKNET "clk_c" ;
diff --git a/demo/lattice_brevia2/mac_test/mac_test1.sty b/demo/lattice_brevia2/mac_test/mac_test1.sty
new file mode 100644
index 0000000..5fe5de0
--- /dev/null
+++ b/demo/lattice_brevia2/mac_test/mac_test1.sty
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/ledcon.vhd b/design/ledcon.vhd
new file mode 100644
index 0000000..5673199
--- /dev/null
+++ b/design/ledcon.vhd
@@ -0,0 +1,77 @@
+-- -------------------------------------------------------------------------- --
+-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
+-- -------------------------------------------------------------------------- --
+-- ledcon.vhd : Simple debug LED helper
+-- To blink or not blink.
+-- -------------------------------------------------------------------------- --
+-- Author : Markus Koch
+-- Contributors : None
+-- License : Mozilla Public License (MPL) Version 2
+-- -------------------------------------------------------------------------- --
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.math_real.all;
+
+entity ledcon is
+ generic(
+ F_CLK : integer;
+ BLINK : boolean_vector
+ );
+ port(
+ clk : in std_logic;
+ rst : in std_logic;
+ sig : in std_logic_vector;
+ led_n : out std_logic_vector
+ );
+end entity ledcon;
+
+architecture RTL of ledcon is
+ constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3))));
+ signal ms_cnt : integer range 0 to MS_CNT_MAX;
+ signal ms_tick : std_logic;
+begin
+ mstimer : process(clk, rst) is
+ begin
+ if rst then
+ ms_cnt <= MS_CNT_MAX;
+ elsif rising_edge(clk) then
+ if (ms_cnt = 0) then
+ ms_cnt <= MS_CNT_MAX;
+ else
+ ms_cnt <= ms_cnt - 1;
+ end if;
+ end if;
+ end process mstimer;
+ ms_tick <= '1' when ms_cnt = 0 else '0';
+
+ ledcongen : for i in sig'low to sig'high generate
+ ledblock : block
+ constant LED_CNT_MAX : integer := 50; -- on time, in ms
+ constant LED_CNT_MIN : integer := -50; -- off time, in ms
+ signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
+ begin
+ ledgen : if BLINK(i) generate
+ ledproc : process(clk, rst) is
+ begin
+ if rst then
+ led_cnt <= LED_CNT_MIN;
+ elsif rising_edge(clk) then
+ if (led_cnt = LED_CNT_MIN) then
+ if sig(i) then
+ led_cnt <= LED_CNT_MAX;
+ end if;
+ elsif ms_tick then
+ led_cnt <= led_cnt - 1;
+ end if;
+ end if;
+ end process ledproc;
+ led_n(i) <= '0' when led_cnt > 0 else '1';
+ else generate
+ led_n(i) <= not sig(i);
+ end generate ledgen;
+ end block ledblock;
+ end generate ledcongen;
+
+end architecture RTL;
diff --git a/design/top_mac_test.vhd b/design/top_mac_test.vhd
new file mode 100644
index 0000000..b1f77ea
--- /dev/null
+++ b/design/top_mac_test.vhd
@@ -0,0 +1,212 @@
+-- -------------------------------------------------------------------------- --
+-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
+-- -------------------------------------------------------------------------- --
+-- top_mac_test.vhd: Test design to verify the functionality of the MAC core
+-- Sends a broadcast with 100 decrementing numbers every 250 ms or when a
+-- button is pushed. It will also directly reply with that content to any
+-- packet addressed to us.
+--
+-- Target: Lattice Brevia 2 (LFXP2-5E)
+-- -------------------------------------------------------------------------- --
+-- Author : Markus Koch
+-- Contributors : None
+-- License : Mozilla Public License (MPL) Version 2
+-- -------------------------------------------------------------------------- --
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+use ieee.math_real.all;
+
+library trashernet;
+use trashernet.trashernet_pkg.all;
+
+entity top_mac_test is
+ port(
+ clk : in std_logic;
+ rst_n : in std_logic;
+ rx_p : in std_logic;
+ tx_p : out std_logic;
+ tx_n : out std_logic;
+ led_n : out std_logic_vector(7 downto 0);
+ button_n : in std_logic_vector(3 downto 0);
+ debug_data : out std_logic_vector(7 downto 0)
+ );
+end entity top_mac_test;
+
+architecture rtl of top_mac_test is
+ component pll0
+ port(
+ CLK : in std_logic;
+ CLKOP : out std_logic;
+ LOCK : out std_logic
+ );
+ end component pll0;
+
+ constant F_CLK : integer := 50000000;
+ constant F_CLK_PHY : integer := 140000000;
+
+ constant LED_BLINK : boolean_vector(led_n'range) := (
+ 6 downto 2 => true,
+ others => false
+ );
+
+ constant ETH_CONFIG : configuration_t := (
+ mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33")
+ );
+
+ signal rst : std_logic;
+ signal clk_phy : std_logic;
+ signal phy_pll_lock : std_logic;
+
+ signal led_sig : std_logic_vector(led_n'range);
+
+ signal phy_out : phy_out_t;
+ signal phy_in : phy_in_t;
+
+ signal mac_out : mac_out_t;
+ signal mac_in : mac_in_t;
+
+ type status_t is (IDLE, TX, WAITDONE);
+ signal state : status_t;
+ constant BYTE_CNT_MAX : integer := 100;
+ signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
+
+ signal button_n_sync : std_logic_vector(button_n'range);
+ signal button : std_logic_vector(button_n'range);
+
+ constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
+ signal tmo : integer range 0 to TMO_MAX;
+begin
+ trashernet_mac_inst : entity trashernet.trashernet_mac
+ port map(
+ clk => clk,
+ rst => rst,
+ phy_out => phy_out,
+ phy_in => phy_in,
+ mac_out => mac_out,
+ mac_in => mac_in
+ );
+
+ pll0_inst : pll0
+ port map(
+ CLK => clk,
+ CLKOP => clk_phy,
+ LOCK => phy_pll_lock
+ );
+
+ trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
+ generic map(
+ F_CLK => F_CLK,
+ F_CLK_PHY => F_CLK_PHY
+ )
+ port map(
+ clk => clk,
+ phy_clk => clk_phy,
+ rst => rst,
+ phy_out => phy_out,
+ phy_in => phy_in,
+ rx_p => rx_p,
+ tx_p => tx_p,
+ tx_n => tx_n
+ );
+
+ reset_sync : process(clk, rst_n) is
+ begin
+ if (rst_n = '0') then
+ rst <= '1';
+ elsif (rising_edge(clk)) then
+ rst <= '0';
+ end if;
+ end process reset_sync;
+
+ ledcon_inst : entity work.ledcon
+ generic map(
+ F_CLK => F_CLK,
+ BLINK => LED_BLINK
+ )
+ port map(
+ clk => clk,
+ rst => rst,
+ sig => led_sig,
+ led_n => led_n
+ );
+
+ led_sig <= (
+ 0 => phy_pll_lock,
+ 1 => phy_out.carrier_detect,
+ 2 => phy_out.rx_active,
+ 3 => phy_out.rx_error,
+ 4 => mac_out.rx_mac_crc_ok,
+ 5 => mac_out.rx_mac_crc_error,
+ 6 => mac_out.tx_active,
+ 7 => '0'
+ );
+
+ synchronizer_inst : entity trashernet.synchronizer
+ generic map(
+ SIZE => 2
+ )
+ port map(
+ clk => clk,
+ rst => rst,
+ data_in => button_n(0),
+ data_out => button_n_sync(0)
+ );
+
+ button <= not button_n_sync;
+
+ receiver : process(clk, rst) is
+ begin
+ if rst then
+ state <= IDLE;
+ mac_in.tx_mac_data_en <= '0';
+ tmo <= 0;
+ elsif rising_edge(clk) then
+ if (tmo /= 0) then
+ tmo <= tmo - 1;
+ end if;
+ case state is
+ when IDLE =>
+ if mac_out.rx_mac_crc_ok then
+ if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts!
+ state <= TX;
+ byte_cnt <= BYTE_CNT_MAX;
+ mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source;
+ end if;
+ end if;
+
+ if (tmo = 0) or (button(0) = '1') then
+ state <= TX;
+ byte_cnt <= BYTE_CNT_MAX;
+ mac_in.tx_header.mac_destination <= (others => x"FF");
+ end if;
+ when TX =>
+ tmo <= TMO_MAX;
+ mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8)));
+ mac_in.tx_mac_data_en <= '1';
+ mac_in.tx_mac_data <= std_logic_vector(to_unsigned(byte_cnt, 8));
+ if mac_out.tx_mac_data_ack then
+ if byte_cnt = 1 then
+ mac_in.tx_mac_data_en <= '0';
+ state <= WAITDONE;
+ else
+ byte_cnt <= byte_cnt - 1;
+ end if;
+ end if;
+ when WAITDONE =>
+ if mac_out.tx_active = '0' then
+ state <= IDLE;
+ end if;
+ end case;
+
+ end if;
+ end process receiver;
+
+ mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address;
+
+ debug_data(0) <= tx_p;
+ debug_data(1) <= tx_n;
+
+end architecture rtl;