From cc4b08817320e8a206f440a0be68d97af711e517 Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Fri, 27 Sep 2024 16:34:55 +0200 Subject: [PATCH] fpga: ram: Use Verilog model for synthesis and VHDL for simulation --- fpga/hdl/design/ram_vhdl.vhd | 94 ++++++++++++++++++++++++ fpga/hdl/design/servant_ram.vhd | 126 +++++++++++++++++--------------- fpga/hdl/design/top.vhd | 3 +- 3 files changed, 165 insertions(+), 58 deletions(-) create mode 100644 fpga/hdl/design/ram_vhdl.vhd diff --git a/fpga/hdl/design/ram_vhdl.vhd b/fpga/hdl/design/ram_vhdl.vhd new file mode 100644 index 0000000..bbc38ec --- /dev/null +++ b/fpga/hdl/design/ram_vhdl.vhd @@ -0,0 +1,94 @@ +-- -------------------------------------------------------------------------- -- +-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- +-- -------------------------------------------------------------------------- -- +-- Servant-compatible RAM, rewritten in VHDL +-- -------------------------------------------------------------------------- -- +-- 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; + +library generics; +use generics.wishbone_pkg.all; + +entity 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 ram_vhdl; + +architecture rtl of ram_vhdl is + type mem_t is array (0 to 2 ** adr_width - 1) of std_logic_vector(wb_i.dat'range); + + impure function init_ram_hex return mem_t is + file text_file : text open read_mode is memfile; + variable text_line : line; + variable ram_content : mem_t := (others => (others => '0')); + begin + for i in 0 to 2 ** adr_width - 1 loop + exit when endfile(text_file); + readline(text_file, text_line); + hread(text_line, ram_content(i)); + end loop; + + return ram_content; + end function; + + signal mem : mem_t := init_ram_hex; + signal mem_address : integer range mem'range; +begin + wb_o.rty <= '0'; + wb_o.err <= '0'; + wb_o.stall <= '0'; + + mem_address <= to_integer(unsigned(wb_i.adr(adr_width - 1 + 2 downto 2))); + + 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 " & 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; + + mem_p : process(clk) is + begin + if rising_edge(clk) then + wb_o.dat <= mem(mem_address); + wb_o.ack <= wb_i.stb; + if (wb_i.we) then + wb_o.dat <= (others => '-'); + for i in wb_i.sel'range loop + if wb_i.sel(i) then + mem(mem_address)((i + 1) * 8 - 1 downto (i * 8)) <= wb_i.dat((i + 1) * 8 - 1 downto (i * 8)); + end if; + end loop; + end if; + + if clr then + wb_o.dat <= (others => '-'); + wb_o.ack <= '0'; + end if; + end if; + end process mem_p; +end architecture rtl; diff --git a/fpga/hdl/design/servant_ram.vhd b/fpga/hdl/design/servant_ram.vhd index f70c445..27468f5 100644 --- a/fpga/hdl/design/servant_ram.vhd +++ b/fpga/hdl/design/servant_ram.vhd @@ -1,7 +1,7 @@ -- -------------------------------------------------------------------------- -- -- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- -- -------------------------------------------------------------------------- -- --- Servant-compatible RAM, rewritten in VHDL +-- TODO -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- Contributors : None @@ -11,16 +11,16 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -use std.textio.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 + memfile : string := "data/empty.hex"; + read_only : boolean := false; + adr_width : integer := 16; + force_vlog : boolean := false ); port( clk : in std_logic; -- CPU and bus clock @@ -32,63 +32,75 @@ entity servant_ram_vhdl is end entity servant_ram_vhdl; architecture rtl of servant_ram_vhdl is - type mem_t is array (0 to 2 ** adr_width - 1) of std_logic_vector(wb_i.dat'range); + 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; - impure function init_ram_hex return mem_t is - file text_file : text open read_mode is memfile; - variable text_line : line; - variable ram_content : mem_t := (others => (others => '0')); - begin - for i in 0 to 2 ** adr_width - 1 loop - exit when endfile(text_file); - readline(text_file, text_line); - hread(text_line, ram_content(i)); - end loop; - - return ram_content; - end function; - - signal mem : mem_t := init_ram_hex; - signal mem_address : integer range mem'range; begin - wb_o.rty <= '0'; - wb_o.err <= '0'; - wb_o.stall <= '0'; + vlogforce : if force_vlog generate + 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 + ); - mem_address <= to_integer(unsigned(wb_i.adr(adr_width - 1 + 2 downto 2))); + 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; - - mem_p : process(clk) is - begin - if rising_edge(clk) then - wb_o.dat <= mem(mem_address); - wb_o.ack <= wb_i.stb; - if (wb_i.we) then - wb_o.dat <= (others => '-'); - for i in wb_i.sel'range loop - if wb_i.sel(i) then - mem(mem_address)((i + 1) * 8 - 1 downto (i * 8)) <= wb_i.dat((i + 1) * 8 - 1 downto (i * 8)); + 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; - end loop; + 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; - if clr then - wb_o.dat <= (others => '-'); - wb_o.ack <= '0'; - end if; - end if; - end process mem_p; + else generate + ram_vhdl_inst : entity work.ram_vhdl + generic map( + memfile => memfile, + read_only => read_only, + adr_width => adr_width + ) + port map( + clk => clk, + clr => clr, + wb_o => wb_o, + wb_i => wb_i + ); + end generate vlogforce; end architecture rtl; diff --git a/fpga/hdl/design/top.vhd b/fpga/hdl/design/top.vhd index dd29383..037885d 100644 --- a/fpga/hdl/design/top.vhd +++ b/fpga/hdl/design/top.vhd @@ -239,7 +239,8 @@ begin generic map( memfile => "../sw/bootrom/bootrom.vhex", read_only => true, - adr_width => 9 + adr_width => 9, + force_vlog => not in_simulation -- GHDL + Yosys doesn't keep the memfile ) port map( clk => clk,