Reading OTP: wrong results depending on clock speed

I’m trying to print the contents of some memory addresses. I got quite unexpected results when doing it as an Arduino sketch:

1000 1F06F
107C FA30037
20000 20400000
21FF4 B
21FF8 20000212
21FFC 28007
20400000 5FC01197
20400004 C2818193
20400008 5FC04117
2040000C FF810113

Writing in plain C using printf gives:

00001000 0001f06f
0000107c 0fa30037
00020000 70500005
00021ff4 0000000b
00021ff8 200002b3
00021ffc 00028027
20400000 5fc01197
20400004 0a818193
20400008 5fc04117
2040000c ff810113

The values in ROM and OTP are not as documented in the manuals, but that’s probably explainable. More worryingly, even the code of my program is printed wrong by the Arduino code! The 2nd (printf) version is as expected:

20400000 <_start>:
20400000:       5fc01197                auipc   gp,0x5fc01
20400004:       0a818193                addi    gp,gp,168 # 800010a8 <_gp>
20400008:       5fc04117                auipc   sp,0x5fc04
2040000c:       ff810113                addi    sp,sp,-8 # 80004000 <_sp>

Oh!

If I run the Arduino sketch at 16 MHz then I get the documented values at 0x1000, 0x20000, 0x21FF4, 0x21FF8. The value at 0x21FFC is 0x28067 not 0x28057.

20000 7F50106F
21FF4 F
21FF8 200002B7
21FFC 28067

Maybe the OTP doesn’t read correctly at high clock speeds. But something else is going on here too. Could be the documentation is simply wrong for 0x21FFC.

(I resolved the difference at 0x20400004 … with differnt code _gp of course ends up pointing to a slightly different place … my bad)

Yes, JALR definitely should end with 0x67, so the documentation for location 0x21FFC is incorrect.
0x57 is a reserved major opcode.

Definitely there is a limit to the OTP clock frequency. Similar to the UART and SPI, when you turn up the clock frequency you have to set the OTP’s Clock Divider as well (unlike SPI and UART, there aren’t handy Arduino libraries to do it for you :slight_smile: )

See the scale field in Figure 14.2 of https://dev.sifive.com/documentation/freedom-e300-platform-reference-manual/

Note that you would have to acquire the OTP lock to set this, as described in 14.1.

The default divide setting is only good to around 20Mhz or so (it’s tuned for the range of expected HFROSC frequencies).

We will post the ROM contents. It contains some test patterns and helpful functions like strcmp, but we don’t plan to update the SDK to make use of them.

Thanks for catching the typo in the Getting Started Guide! We’ll update it.

Thanks Megan. I’m primarily running code using the freedom-e-sdk with “make upload PROGRAM=$(TARGET)” etc. That’s running at 256 MHz regardless of what I’ve done previousl yin the Arduino environment. I don’t know how to change the clock frequency when using the freedom-e-sdk. I’m certainly not changing anything like that myself with my little programs doing a few printf()s in main!

While you’re there … what happens before starting code execution from 0x1000? Something must be setting up the SPI, especially for the memory mapping, but that’s not been documented that I can find. The long 1.5 second or so delay before running user code happens even if the user code is linked to load at 0x20000000 overwriting the “bootloader”. Documentation implies that the delay is in the bootloader, but that doesn’t seem to be true.

Right. We recently added some helpful functions for setting the clock sources: https://github.com/sifive/freedom-e-sdk/blob/master/bsp/drivers/fe300prci/fe300prci_driver.h. But the SDK doesn’t actually use them.

In the Freedom E SDK, the “init.c” code switches over to the PLL clock source (with the HFROSC as the input), and the code there sets the SPI clock divider high out of paranoia since we know we are running out of SPI, and sets the UART appropriately so printf will just work. We’re not expecting to do anything with OTP in general, so we didn’t make the corresponding change to the OTP’s clock divider.

The PLL code is in init rather than in main so that it can be applied for the benchmark codes, which don’t allow us to change main.

The majority of the time before code execution at 0x1000 is the internal “reset stretcher” logic inside the chip. When you release the reset button, this circuit inside the chip is tuned to wait 1-2s before deasserting the reset to the AON block. This is to give time for the LFROSC which AON is running off of to be nice and stable. Once the AON block is running, it spends a few of its slow (32kHz) cycles taking the rest of the chip out of reset. (note that actually on the FE310 the LFROSC isn’t used, there is an external LFOSC).

Once the CPU is out of reset, it starts running at 0x1000. It is running off its HFROSC source which is not particularly accurate, at 12-20MHz or so. So all the code sources that we might start fetching from at reset (mask ROM, OTP, and mem-mapped SPI) have their clock divider settings with this in mind.

The mem-mapped SPI doesn’t need any software set up?

Nope. The FE310 chip itself is perfectly capable of booting straight from external SPI, but the pins that select that choice aren’t exported to the 48-pin package.

Slight correction - the long reset delay is to allow an external MEMS/crystal oscillator to settle, the internal LFROSC is up and running much faster.