-
Hi there, is there a recommended way to get multiple independent instantiations of neorv32 in one FPGA design, running different software from their independent IMEM ROMs? The code base seems to support only one imem instantiation, i.e. having a neorv32_application_image.vhd with a constant application_init_image. These modifications work but obviously I have to patch through the hierarchy. Is there a better way that I do not see? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
Hey there!
Unfortunately, this is correct. The only way to boot several cores with different code is to make the cores fetch their code from "somewhere" (external flash, a global on-chip memory, etc.; see data sheet section Boot Configuration). I do not think there is a straightforward "out of the box" solution for this yet and we already had a discussion about this a long time ago in #35. Maybe we could change the VHDL code of the processor-internal IMEM (and the bootloader ROM) to add some simple support for this kind of use cases:
I am not sure about this, but I think managing just some HEX files would be way simpler than doing library/package hacks... 🤔 |
Beta Was this translation helpful? Give feedback.
-
I just tested the initialization of the IMEM from a HEX file using the following function (works!): impure function init_imem_hex(file_name : string; num_words : natural) return mem32_t is
file text_file : text open read_mode is file_name;
variable text_line_v : line;
variable mem_v : mem32_t(0 to num_words-1);
variable index_v : natural;
variable char_v : character;
variable data_v : std_ulogic_vector(31 downto 0);
begin
mem_v := (others => (others => '0'));
index_v := 0;
while not endfile(text_file) loop
readline(text_file, text_line_v);
-- construct one 32-bit word --
data_v := (others => '0');
for i in 7 downto 0 loop -- 32-bit = 8 hex chars
read(text_line_v, char_v); -- get one hex char
case char_v is
when '0' => data_v(i*4+3 downto i*4) := x"0";
when '1' => data_v(i*4+3 downto i*4) := x"1";
when '2' => data_v(i*4+3 downto i*4) := x"2";
when '3' => data_v(i*4+3 downto i*4) := x"3";
when '4' => data_v(i*4+3 downto i*4) := x"4";
when '5' => data_v(i*4+3 downto i*4) := x"5";
when '6' => data_v(i*4+3 downto i*4) := x"6";
when '7' => data_v(i*4+3 downto i*4) := x"7";
when '8' => data_v(i*4+3 downto i*4) := x"8";
when '9' => data_v(i*4+3 downto i*4) := x"9";
when 'a' | 'A' => data_v(i*4+3 downto i*4) := x"a";
when 'b' | 'B' => data_v(i*4+3 downto i*4) := x"b";
when 'c' | 'C' => data_v(i*4+3 downto i*4) := x"c";
when 'd' | 'D' => data_v(i*4+3 downto i*4) := x"d";
when 'e' | 'E' => data_v(i*4+3 downto i*4) := x"e";
when 'f' | 'F' => data_v(i*4+3 downto i*4) := x"f";
when others => data_v(i*4+3 downto i*4) := x"0";
end case;
end loop; -- i
-- store according byte to memory image --
mem_v(index_v) := data_v;
index_v := index_v + 1;
end loop; -- not end of file
return mem_v;
end function init_imem_hex; The you can initialize the IMEM's constant mem_rom : mem32_t(0 to IMEM_SIZE/4-1) := init_imem_hex("/mnt/n/Projects/neorv32/sw/example/hello_world/neorv32_raw_exe.hex", IMEM_SIZE/4); |
Beta Was this translation helpful? Give feedback.
-
Cool, thanks for your effort. I will test this in the next days. There seem to be also some other promising approaches ( see the discussion I started here in German https://www.mikrocontroller.net/topic/541850 ). Especially the separation by different libraries as in the initial post or using configurations as proposed later in the thread above may have the advantage that the sources and workflow don't need to be changed much* (and additionally even a different IMEM architecture may be used per neorv32 instance**). Thanks for your great work! *Independent from this discussion, it may be worth considering to rename the "library neorv32; use neorv32..." into "library work; use work..." in the core source files. So the user can decide into which library to compile or compile into multiple libraries. **Imagine using an IMEM architecture with instantiated BRAM primitives for synthesis that is not initialized but can be updated on synthesis/par result on bitstream creation for dedicated instances [to avoid complete HDL resynth/par of large HDL designs on neorv32 software change during development] while additionally using more comfortable default inferred RAM architecture with the initialization we have now for other instances [with final or simpler software that doesnt need updates] or for instances used in testbenches only. |
Beta Was this translation helpful? Give feedback.
Hey there!
Unfortunately, this is correct. The only way to boot several cores with different code is to make the cores fetch their code from "somewhere" (external flash, a global on-chip memory, etc.; see data sheet section Boot Configuration). I do not think there is a straightforward "out of the box" solution for this yet and we already had a discussion about this a long time ago in #35.
Maybe we could change the VHDL code of the processor-internal IMEM (and the bootloader ROM) to add some simple support for this kind of use cases: