Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Processor Boot Concept #62

Closed
stnolting opened this issue Jun 10, 2021 · 18 comments
Closed

Processor Boot Concept #62

stnolting opened this issue Jun 10, 2021 · 18 comments
Assignees
Labels
question Further information is requested

Comments

@stnolting
Copy link
Owner

This is a "design note" inspired by #56 (especially #56 (comment) and #56 (comment)).

Currently, the processor provides 3 boot options:

  1. Use the bootloader (BOOTLOADER_EN => true) to upload executables via UART or from external SPI flash. In this scenario the internal IMEM is implemented as UNinitialized RAM.
  2. No bootloader (BOOTLOADER_EN => true). IMEM is implemented as initialized RAM (initialized with actual executable). The content of the RAM can be modified by software as it is a RAM.
  3. No bootloader (BOOTLOADER_EN => true). IMEM is implemented as initialized ROM (MEM_INT_IMEM_ROM => true, initialized with actual executable).

I do not see any practical use case for option 2, so I think this could be removed?! 🤔

If option 2 is not needed anymore, the boot configuration could be simplified by replacing BOOTLOADER_EN and MEM_INT_IMEM_ROM generics by a single boot config generic (what name? what type?).

@stnolting stnolting added enhancement New feature or request question Further information is requested labels Jun 10, 2021
@stnolting stnolting self-assigned this Jun 10, 2021
@stnolting stnolting mentioned this issue Jun 10, 2021
Closed
@umarcor
Copy link
Collaborator

umarcor commented Jun 10, 2021

I'm a graphical person, so I did the following diagram:

neorv32_boot

  • Gray boxes are ROMs and need to be initialised in the bitstream, since there is no explicit mechanism for loading them at runtime.
  • Orange boxes can be either ROMs or RAMs, and can be empty or initialised. Arrows show which can be loaded at runtime.
  • Blue boxes are RAMs, and can be empty or initialised.
B BD I ID BI BI-U BI-S BID BID-U BID-S
BOOTLOADER_EN true true false false true true true true true true
MEM_INT_IMEM_EN false false true true true true true true true true
MEM_INT_IMEM_ROM - - true true true false false true false false
MEM_INT_DMEM_EN false true false true false false false true true true
IO_UART0_EN - - - - - true - - true -
IO_SPI_EN - - - - - - true - - true

I'm not sure about loading DMEM through the bootloader. How does that work? The bootloader initializes both IMEM and DMEM? Does it initialize IMEM only, and the software app from IMEM then initializes DMEM as part of the "app startup"?

@stnolting
Copy link
Owner Author

Holy cricket! 😄 You really created some diagrams?! That's real cool! 👍

First of all: When I talk about "IMEM" and "DMEM" I mean the processor-internal ones 😅 - so not any memory component that is coupled via Wishbone/AXI. This implies MEM_INT_IMEM_EN and MEM_INT_DMEM_EN to be true. I would keep these two generics to allow the user if processor-internal or processor-external instances are preferred. However, this does not effect the general boot process: for example if MEM_INT_IMEM_EN is false the bootloader (if enabled) would store the executable via Wishbone/AXi to the external memory. If there is no bootloader, the user has to make sure the external IMEM is somehow initialized because the CPU will start booting from there.

Second: From a software point of view setups without DMEM (internal or external) won't work (at least not for the normal user) because you need that for storing runtime data like the stack.

As a result, only setups ID and BID* seem to be reasonable:
121559487-e3827100-ca16-11eb-8886-ed7b1ddce08c

It seems we would only need a single generic to configure the boot process.
(for now I would like to ignore the IO_UART0_EN and IO_SPI_EN generics of the BID setup).

I'm not sure about loading DMEM through the bootloader. How does that work? The bootloader initializes both IMEM and DMEM? Does it initialize IMEM only, and the software app from IMEM then initializes DMEM as part of the "app startup"?

You are right.

  • When using the bootloader: Bootloader initializes IMEM (executable upload from UART/SPI); bootloader transfers control to the application in IMEM; application in IMEM initializes the DMEM
  • When not using the bootloader: application in IMEM initializes the DMEM

@stnolting
Copy link
Owner Author

Oh, one thing I forgot...

Orange boxes can be either ROMs or RAMs, and can be empty or initialised. Arrows show which can be loaded at runtime.

Do you mean there are two configurations?

  • empty RAM
  • initialized ROM

Or four configurations??

  • empty ROM
  • initialized ROM
  • empty RAM
  • initialized ROM

I think the two configurations option makes more sense. 🤔

@tmeissner
Copy link
Collaborator

I wonder how FPGA-architectures without pre-loadable RAM/ROM fit in the boot concept. For example, we use Microchip PolarFire which has BRAMs that cannot be loaded by bitstream. But it has a NVM, which can be programmed by the bitstream. So we would try to use the NVM to store the program and then try to load the IMEM from the NVM at start-up. Is this currently easily doable?

@stnolting
Copy link
Owner Author

I wonder how FPGA-architectures without pre-loadable RAM/ROM fit in the boot concept.

The last resort is to use LUTs for implementing the ROM.

So we would try to use the NVM to store the program and then try to load the IMEM from the NVM at start-up. Is this currently easily doable?

Sure. You could write a wrapper for the NVW and replace the default neorv32_imem.vhd memory module. Thus, the CPU would directly fetch and execute instructions from the NVW.

Another option would be to couple the NVM via Wishbone/AXI and rewrite the bootloader to just contain a "memcpy()" that moves data from NVM to the (uninitialized) IMEM (RAM). Such a simple bootloader code should fit into a pretty small ROM (maybe ~64 bytes) that can be implemented using LUTs.

@umarcor
Copy link
Collaborator

umarcor commented Jun 10, 2021

So, I updated the diagram, taking into account that DMEM is always required and that both IMEM and DMEM can be either internal or external:

neorv32_boot

  • Green boxes are external and IMEM needs to be initialised externally or through the bitstream, unless the bootloader is enabled.
  • Gray boxes are ROMs and need to be initialised in the bitstream, since there is no explicit mechanism for loading them at runtime.
  • Blue boxes are RAMs, and can be empty or initialised.
  • Orange boxes can be either ROMs or RAMs, can be empty or initialised, can be internal or external. Arrows show which can be loaded at runtime.

As a result, only setups ID and BID* seem to be reasonable:

I think it is relevant to explain the difference between N/D, I/ID and B/BD, which is the size of the main/only program memory:

  • In N/D, the size and implementation is controlled by the user according to the external interface.
  • In I/ID, the implementation needs to be done through the IMEM entity defined by NEORV32; the size is limited to 2GB.
  • In B/BD, the implementation is built-in, and the size is limited to 32KB.

Do you mean there are two configurations?

Or four configurations??

In the orange boxes, I mean three options:

  • initialized ROM
  • initialized RAM
  • empty RAM

It might seem that having a ROM IMEM along with the bootloader might not make sense. However, that can be combined with an external IMEM (E-IMEM). So, at startup, the bootloader would check if a new application is to be loaded. If so, it would load it to E-IMEM, and jump there. Otherwise, it would fall back to the internal IMEM.

@umarcor
Copy link
Collaborator

umarcor commented Jun 10, 2021

But it has a NVM, which can be programmed by the bitstream. So we would try to use the NVM to store the program and then try to load the IMEM from the NVM at start-up. Is this currently easily doable?

This is what I/ID try to illustrate, in comparison to B/BD. The BootROM implementation is not expected to be customised, but the IMEM implementation is replaceable (as we are doing for radiant and ghdl-yosys-nextpnr).

@umarcor umarcor mentioned this issue Jun 10, 2021
@stnolting
Copy link
Owner Author

I am still trying to figure out some exemplary use cases for your updated memory configs 😅

I just want to emphasize one point again: the CPU does not care where it fetches data or instructions from. This is not a fixed Harvard architecture - it is more like a modified von-Neumann architecture. There is no such thing like an exclusive address space (=memory) for instructions or data. "IMEM" and "DMEM" are just "user" or "software" concepts.

So one possible setup can be (assuming customized linker script, bootloader, etc.):

  • no external memories via Wishbone/AXI
  • only implement processor-internal DMEM; no processor-internal IMEM
  • bootloader loads executable from somewhere, stores it to internal DMEM
  • executable is entirely executed from internal DMEM (all runtime data is in here, too)

It might seem that having a ROM IMEM along with the bootloader might not make sense. However, that can be combined with an external IMEM (E-IMEM). So, at startup, the bootloader would check if a new application is to be loaded. If so, it would load it to E-IMEM, and jump there. Otherwise, it would fall back to the internal IMEM.

Good point. 🤔

I know there are a lot of different concepts for defining a boot process. But maybe the processor setup does not have to provide pre-defined options for all of them. I think it would be sufficient to provide something like "the most common" ones (I know, this is relative):

  1. use internal bootloader; executable is stored to int-IMEM if implemeted or ext-IMEM if not; int-DMEM is used for data if it is implemented, otherwise ext-DMEM is used
  2. no bootloader; if int-IMEM is implemented it is implemented as ROM an initialized with the executable; int-DMEM is used for data if it is implemented, otherwise ext-DMEM is used
  3. if none of the previous 2 options fits: just provide a generic to specify the boot address manually. By this the user has maximum flexibility

@umarcor
Copy link
Collaborator

umarcor commented Jun 10, 2021

I am still trying to figure out some exemplary use cases for your updated memory configs 😅

The point is that I am not proposing new use cases to be supported, but just trying to understand/describe what's possible already 😆 . I agree that some of those are not the most expected, and we don't need to explicitly explain more than 2-3 in the user guide. However, I think there should be some place where the flexibility is made explicit.

I just want to emphasize one point again: the CPU does not care where it fetches data or instructions from. This is not a fixed Harvard architecture - it is more like a modified von-Neumann architecture. There is no such thing like an exclusive address space (=memory) for instructions or data. "IMEM" and "DMEM" are just "user" or "software" concepts.

Understood. Yet, my concern is the hardware point of view. As you can see in the diagrams, I care about how is each of the memories described in hardware, how is it connected, and how is it initialised. I'm a hardware guy 😄. So, even if IMEM and DMEM can be the same for the software, they are different for me from the hardware point of view. For instance, I can load the main app to the BootROM and replace IMEM with a custom peripheral, so that I have an additional directly accessible interface.

Being von-Neumann, the CPU can work with a single memory which is used as IMEM and DMEM at the same time, isn't it?

So one possible setup can be (assuming customized linker script, bootloader, etc.):

  • no external memories via Wishbone/AXI
  • only implement processor-internal DMEM; no processor-internal IMEM
  • bootloader loads executable from somewhere, stores it to internal DMEM
  • executable is entirely executed from internal DMEM (all runtime data is in here, too)

From a software point of view, that is exactly equivalent to having internal IMEM and no internal DMEM, isn't it? The difference in hardware is that IMEM can be a ROM and DMEM cannot.

I know there are a lot of different concepts for defining a boot process. But maybe the processor setup does not have to provide pre-defined options for all of them. I think it would be sufficient to provide something like "the most common" ones (I know, this is relative):

In fact, I don't think the processor setup needs to have pre-defined options for any of them. I'm good with having a table such as the one above in the documentation, where we highlight the most common processes. Then, for each board, we would select the solution that best fits. For instance, on the Fomu, the best solution is very different if an USB-to-UART component is available, or if the external SPI is used, compared to not using/having any of those resources.

  1. use internal bootloader; executable is stored to int-IMEM if implemeted or ext-IMEM if not; int-DMEM is used for data if it is implemented, otherwise ext-DMEM is used
  2. no bootloader; if int-IMEM is implemented it is implemented as ROM an initialized with the executable; int-DMEM is used for data if it is implemented, otherwise ext-DMEM is used
  3. if none of the previous 2 options fits: just provide a generic to specify the boot address manually. By this the user has maximum flexibility

These do not cover the use case that brought us here 😄: using the bootloader for the (up to 32KB) main/only app; without IMEM (neither internal nor external).

provide a generic to specify the boot address manually

I think this is an interesting feature in any case.

@stnolting
Copy link
Owner Author

However, I think there should be some place where the flexibility is made explicit.

Absolutely! 👍

Understood. Yet, my concern is the hardware point of view. As you can see in the diagrams, I care about how is each of the memories described in hardware, how is it connected, and how is it initialised. I'm a hardware guy 😄. So, even if IMEM and DMEM can be the same for the software, they are different for me from the hardware point of view. For instance, I can load the main app to the BootROM and replace IMEM with a custom peripheral, so that I have an additional directly accessible interface.

I just wanted to underline the fact that boot process is completely up to the (mercy of the) user.
Btw, don't think I am a software guy. I know how to write some C code - but that's all 😅

Being von-Neumann, the CPU can work with a single memory which is used as IMEM and DMEM at the same time, isn't it?

Correct.
I just thought it would me more "intuitive" to have separated memories for instruction and data. Furthermore, this separated approach allows to place some "dead address space" between those memories. By this a thing like a stack overflow can get caught by the runtime environment and does not override your code / directly crash the processor.

From a software point of view, that is exactly equivalent to having internal IMEM and no internal DMEM, isn't it? The difference in hardware is that IMEM can be a ROM and DMEM cannot.

Oh, yes, you are right.
Seems like I am getting stuck in my own IMEM/DMEM constraints. 😄

These do not cover the use case that brought us here 😄: using the bootloader for the (up to 32KB) main/only app; without IMEM (neither internal nor external).

Yeah but no...
The second boot scenario should work for the FOMU - at least in theory, right?
I mean, this is just a toolchain problem right now and maybe there is a workaround.

I would like to remove the "initialized IMEM as RAM" concept, since it does not make any sense to me.
When removed, we would have something like a Harvard architecture as boot alternative (IMEM = ROM = code; DMEM = RAM = empty). I hope you understand what I mean 😉

Btw, I am still experimenting with memories, but these seems to work:

@stnolting stnolting removed the enhancement New feature or request label Jun 11, 2021
stnolting added a commit that referenced this issue Jun 11, 2021
@stnolting
Copy link
Owner Author

How about this:

Memory Setup

There are three internal memories right now:

  • IMEM for actual to be executed; this can either be a RAM or a ROM
  • DMEM for runtime data; this is always a RAM
  • BOOTROM for the bootloader code; this is always a ROM

I would like to constrain these internal memory types:

  • RAMs are never initialized during synthesis
  • ROMs are always initialized during synthesis

Using only internal memories there would be two boot configurations:

  1. Bootloader: IMEM is a RAM; bootloader (in BOOTROM) fetches code from somewhere (e.g., external SPI flash), stores that to IMEM and executes it; this is the default boot configuration
  2. some-fancy-name: IMEM is a ROM and pre-intialized with application code; BOOTROM is not implemented at all

Different "memories" could still be used to customize the boot process: Either by replacing the default IMEM / DMEM VHDl source files (the ice40up setups do that right now) or by attaching custom memory modules via the bus interface and using these modules for instruction/data storage via the MEM_INT_IMEM_EN and MEM_INT_DMEM_EN generics.

Boot Condiguration

Remove BOOTLOADER_EN and MEM_INT_IMEM_ROM generics. Add a new generic BOOT_CONFIG to define the actual boot configuration (see above). But what type could we use?

  • An integer with options 1,2,3,... does not seem to be intuitive.
  • Maybe a string? So we could pass something like "bootloader". A custom string_compare function would be used to deal with different string lengths and uppercase/lowercase characters.
  • A custom enumeration type. That would require to include neorv32 library when instantiating the processor top.

🤔

@umarcor
Copy link
Collaborator

umarcor commented Jun 12, 2021

I just wanted to underline the fact that boot process is completely up to the (mercy of the) user.

Gotcha. That was actually something I learnt in this issue 😉. I mean, I knew it was open, but not as flexible as it is!

Btw, don't think I am a software guy. I know how to write some C code - but that's all 😅

Oh, I didn't want to imply you are a software guy 😆 but that I feel very unconfident in that area...
I know how to write C code, but I would not say I'm good at all.

I just thought it would me more "intuitive" to have separated memories for instruction and data. Furthermore, this separated approach allows to place some "dead address space" between those memories. By this a thing like a stack overflow can get caught by the runtime environment and does not override your code / directly crash the processor.

I think this is very interesting to be explicitly explained in the docs. It was obvious to me that separated I and D mems could allow having a ROM IMEM and a RAM DMEM. But it was not obvious to me this "defensive" application by introducing a dead address space.

The second boot scenario should work for the FOMU - at least in theory, right?
I mean, this is just a toolchain problem right now and maybe there is a workaround.

In theory... but theory and practice do not always match. I found three different results on Fomu while trying the same practical solution: a single ROM initialised with the application software (not the bootloader software). One of the solutions required 97% of the resources, the other 51% and the last one 49%. The difference between 51% and 49% was using NoBootROM+IMEMROM or BootROM+NOIMEM. So, even though both of those solutions are exactly equivalent from a software point of view, there is some non negligible difference in the hardware that is generated.
Moreover, the difference between 97% and 51% for the NoBootROM+IMEMROM setup was related to the mem inference code I used (default or alternative).

Hence, if we can get the IMEM ROM only design to be equivalent to the Boot ROM only design (i.e. 49%), I'm ok with not supporting the latter. We need to understand where is that difference coming from, though.

I would like to remove the "initialized IMEM as RAM" concept, since it does not make any sense to me.

As commented later, IMAM as RAM is a requierement if the bootloader is used.

How about this

My understanding is that you want both IMEM and DMEM to be always required. Then, whether IMEM is a ROM or a RAM is defined implicitly by the existence of the bootloader. I think that is ok. My point of view was complementary: make DMEM required only, and make IMEM an optional RAM only. However, I understand that my approach would constraint the maximum size of the software, while yours does not.

Again, I'm only concerned with the size difference. Maybe that is because BootROM is limited to 32KB and IMEM can be up to 2GB?

ROMs are always initialized during synthesis

Note that this is not possible on all FPGAs. We need to take into account use cases where the bootloader is, in fact, an RTL module that will read data from any external interface and preload the IMEM with that. From a software point of view, this is the same as not having a BootROM and the IMEM being a ROM. However, from a hardware point of view, there is component which needs access to the internal bus, for writing into the memory.

This is actually a use case I know some users of NEORV32 do have, or will have in the near future. Not something we need to solve now, but we should not forget about it.

Remove BOOTLOADER_EN and MEM_INT_IMEM_ROM generics. Add a new generic BOOT_CONFIG to define the actual boot configuration (see above). But what type could we use?

A custom enumeration type. That would require to include neorv32 library when instantiating the processor top.

The idiomatic solution is a custom enumeration type. The neorv32 library is already an indirect dependency of any synthesis/simulation implying the NEORV32. I think it is fair to require it for instantiating the templates or the top.

I'm not sure about implementing the BOOT_CONFIG in the templates or in the top, though. If done in the templates, that would imply some duplication, but it would not modify the codebase of the CPU/SoC. The duplication can be handled through helper functions in a neorv32.boot_config package. I guess you know the complexity better for evaluating that.

Furthermore, the BOOT_CONFIG might be not only an enumeration, but a record with several fields. Apart from using the bootloader or not, I see it might be interesting to specify the default boot address, the origin and/or destination addressed for the bootloader, the size of the firmware, etc. Again, this additional metadata is already provided in some other way. Furthermore, all of those fields might be overriden in software. Therefore, it's again up to you, the expert. I'm just thinking loud here.

@stnolting
Copy link
Owner Author

I think this is very interesting to be explicitly explained in the docs. It was obvious to me that separated I and D mems could allow having a ROM IMEM and a RAM DMEM. But it was not obvious to me this "defensive" application by introducing a dead address space.

👍 I will add a note to the documentation.

In theory... but theory and practice do not always match. I found three different results on Fomu while trying the same practical solution: a single ROM initialised with the application software (not the bootloader software). One of the solutions required 97% of the resources, the other 51% and the last one 49%. The difference between 51% and 49% was using NoBootROM+IMEMROM or BootROM+NOIMEM.

This is interesting. Maybe it's a thing the bootROM being implement as arrays of 32-bit while the current IMEM ROM is implemented as four arrays of 8-bit. 🤔

Hence, if we can get the IMEM ROM only design to be equivalent to the Boot ROM only design (i.e. 49%), I'm ok with not supporting the latter. We need to understand where is that difference coming from, though.

Good point! I think I will update the IMEM, DMEM and BOOTROM memories. I have already tested some new approaches and these seem to be the best ones:

As commented later, IMAM as RAM is a requierement if the bootloader is used.

Right. But I was pointing at the "initialized" thing. IMEM will be RAM, but it will be "blank" RAM.

ROMs are always initialized during synthesis

Note that this is not possible on all FPGAs. We need to take into account use cases where the bootloader is, in fact, an RTL module that will read data from any external interface and preload the IMEM with that. From a software point of view, this is the same as not having a BootROM and the IMEM being a ROM. However, from a hardware point of view, there is component which needs access to the internal bus, for writing into the memory.

That should not be a problem. A user could use a simplified version of the bootloader that - for example - just loads some exe from an external SPI flash. This could be done with ~64 bytes (optimistic) of bootloader code. I think that every toolchain - regardless of the actual FPGA architecture - should be able to build a LUT-only ROM from this. 🤔

The idiomatic solution is a custom enumeration type. The neorv32 library is already an indirect dependency of any synthesis/simulation implying the NEORV32. I think it is fair to require it for instantiating the templates or the top.

Ok, then this approach wins 😄

I'm not sure about implementing the BOOT_CONFIG in the templates or in the top, though. If done in the templates, that would imply some duplication, but it would not modify the codebase of the CPU/SoC. The duplication can be handled through helper functions in a neorv32.boot_config package. I guess you know the complexity better for evaluating that.

I think I would prefer this directly for the top as the ROM option for the IMEM would be redundant.

The BOOT_CONFIG could be hidden in the setup-specific wrappers.

Furthermore, the BOOT_CONFIG might be not only an enumeration, but a record with several fields. Apart from using the bootloader or not, I see it might be interesting to specify the default boot address, the origin and/or destination addressed for the bootloader, the size of the firmware, etc. Again, this additional metadata is already provided in some other way. Furthermore, all of those fields might be overriden in software. Therefore, it's again up to you, the expert. I'm just thinking loud here.

I agree. But I think it is a good start to begin with just the two aforementioned boot configuration. If they prove to be practical we can add further customization options.

@umarcor
Copy link
Collaborator

umarcor commented Jun 12, 2021

This is interesting. Maybe it's a thing the bootROM being implement as arrays of 32-bit while the current IMEM ROM is implemented as four arrays of 8-bit. 🤔

I thought about that, but it cannot be that only. The byte enable is 4 gates only. Say there is a 4*1024 byte memory. That can be implemented through eight 512x8 BRAMs. Having 4 gates should be absolutely negligible, because the byte enables are evaluated before routing the wr_en or rd_en signal to the BRAM.

However, the address might be relevant. If the width of the address of the IMEM ROM is larger that the one of the BootROM, that size is not optimised, all the bits are routed to the BRAM. Nonetheles, by looking at the sources of the memories, the size of those addresses is extracted from the generics. Need to look deeper into it...

Hence, if we can get the IMEM ROM only design to be equivalent to the Boot ROM only design (i.e. 49%), I'm ok with not supporting the latter. We need to understand where is that difference coming from, though.

Good point! I think I will update the IMEM, DMEM and BOOTROM memories. I have already tested some new approaches and these seem to be the best ones:

Those look nice 😄 Looking forward to testing them.

You might want to describe the ram_t as a unconstrained array of a constrained type (std_logic_vector(31 downto 0)). That should allow to write the init function once only, instead of duplicating the type and function in all the sources.

Right. But I was pointing at the "initialized" thing. IMEM will be RAM, but it will be "blank" RAM.

👍🏼

That should not be a problem. A user could use a simplified version of the bootloader that - for example - just loads some exe from an external SPI flash. This could be done with ~64 bytes (optimistic) of bootloader code. I think that every toolchain - regardless of the actual FPGA architecture - should be able to build a LUT-only ROM from this. 🤔

My point is that the software bootloader does not need hardware access to the IMEM/bus. The BootROM is an slave. The CPU reads the code from that slave, executes it and uses the other peripherals for getting data and writing it to the IMEM.

However, a hardware bootloader needs to have master access to the IMEM/bus, because the CPU cannot fetch the software from any pre-initialised memory. Having a LUT-only ROM is an option, but that would only work if the bootloader is short enough and if it can be used with the built-in peripherals (SPI, UART). If data is to be loaded from some unique interface (not supported as a CPU peripheral), the CPU is useless until the firmware is in place. Naturally, you could argue that this is not to be a suppoert use case, and an external IMEM should be used for this.

I think I would prefer this directly for the top as the ROM option for the IMEM would be redundant.

Good point.

But I think it is a good start to begin with just the two aforementioned boot configuration. If they prove to be practical we can add further customization options.

Absolutely.

stnolting added a commit that referenced this issue Jun 12, 2021
currently, this cannot be covered by the riscv-arch-test due to boot concept issues #62
@stnolting stnolting mentioned this issue Jun 12, 2021
@stnolting
Copy link
Owner Author

However, the address might be relevant. If the width of the address of the IMEM ROM is larger that the one of the BootROM, that size is not optimised, all the bits are routed to the BRAM. Nonetheles, by looking at the sources of the memories, the size of those addresses is extracted from the generics. Need to look deeper into it...

I know that you can have a look at something like an RTL viewer, right? But is there something like a technology viewer to see actual mapping results? 🤔

You might want to describe the ram_t as a unconstrained array of a constrained type (std_logic_vector(31 downto 0)). That should allow to write the init function once only, instead of duplicating the type and function in all the sources.

Good point! I have included that.

My point is that the software bootloader does not need hardware access to the IMEM/bus. The BootROM is an slave. The CPU reads the code from that slave, executes it and uses the other peripherals for getting data and writing it to the IMEM.

However, a hardware bootloader needs to have master access to the IMEM/bus, because the CPU cannot fetch the software from any pre-initialised memory. Having a LUT-only ROM is an option, but that would only work if the bootloader is short enough and if it can be used with the built-in peripherals (SPI, UART). If data is to be loaded from some unique interface (not supported as a CPU peripheral), the CPU is useless until the firmware is in place. Naturally, you could argue that this is not to be a suppoert use case, and an external IMEM should be used for this.

Right. You need software to load software 😄

When you use a boot concept that does not use the internal bootloader and fetches an executable from outside of the processor it is up to the system designer to provide a mechanism to initialize the according memory. I have tried to address this in two new sections of the datasheet:

I still need to write something regarding this for the user guide.

This whole boot concept is pretty complex as there are dozens of different approaches that might fit a very specific setup. I think it as sufficient (for now) to directly support the most common scenarios and to give some information for implementation more application-tailored setups.

@umarcor
Copy link
Collaborator

umarcor commented Jun 16, 2021

I know that you can have a look at something like an RTL viewer, right? But is there something like a technology viewer to see actual mapping results? 🤔

I'd say it's the other way. It's easier to get a technology viewer (or an RTL that is so close to being a technology viewer), rather than an RTL view closed to the HDL description. See ghdl/ghdl#1519 (comment).

I used yosys and netlistsvg in ghdl/ghdl#1783. There are different options you can use, for getting different levels of details and/or symbols:

This whole boot concept is pretty complex as there are dozens of different approaches that might fit a very specific setup. I think it as sufficient (for now) to directly support the most common scenarios and to give some information for implementation more application-tailored setups.

I really like the updated docs (https://stnolting.github.io/neorv32/#_memory_configuration and https://stnolting.github.io/neorv32/#_boot_configuration)! Neither too simple nor too complex,

@stnolting
Copy link
Owner Author

stnolting commented Jun 17, 2021

I used yosys and netlistsvg in ghdl/ghdl#1783. There are different options you can use, for getting different levels of details and/or symbols:

Looks nice! I need to check that out.

I really like the updated docs (https://stnolting.github.io/neorv32/#_memory_configuration and https://stnolting.github.io/neorv32/#_boot_configuration)! Neither too simple nor too complex,

Thank your very much! I really appreciate that. I need to do some more fine tuning their, but I think the general concept(s) should be clear now 😉

@stnolting
Copy link
Owner Author

The boot concept(s) seem to be clear now so I think this can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants