Documentation for the Freedom Metal library can be found here.
sifive / freedom-metal Goto Github PK
View Code? Open in Web Editor NEWBare Metal Compatibility Library for the Freedom Platform
License: Other
Bare Metal Compatibility Library for the Freedom Platform
License: Other
Documentation for the Freedom Metal library can be found here.
@e-puerto reports that the LED polarity on HiFive1 Rev B is inverted. Calling metal_led_on()
turns it off, and vice-versa.
The DeviceTree nodes for LEDs should indicate when their polarity is inverted (writing "high" to the GPIO pin turns them off) and the driver should invert its GPIO output.
ERROR:../../../metal/include/metal/interrupt.h:169:11: error: parameter 'mode' not found in the function declaration [-Werror,-Wdocumentation]
* @param mode The vector mode of the interrupt controller.
^~~~
ERROR:../../../metal/include/metal/interrupt.h:285:11: error: parameter 'threshold' not found in the function declaration [-Werror,-Wdocumentation]
* @param threshold The interrupt threshold level
^~~~~~~~~
../../../metal/include/metal/interrupt.h:285:11: note: did you mean 'level'?
* @param threshold The interrupt threshold level
^~~~~~~~~
level
ERROR:../../../metal/include/metal/interrupt.h:386:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:387:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:386:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:393:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:394:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:393:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:401:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:402:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:401:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:409:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:410:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:409:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:417:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:418:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:417:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:424:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:425:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:424:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:431:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:432:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:431:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:438:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:439:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:438:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:445:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:446:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:445:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:452:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:453:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:452:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:459:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:460:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:459:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:466:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:467:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:466:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:473:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:474:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:473:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:480:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:481:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:480:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:487:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:488:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:487:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:494:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:495:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:494:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:501:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:502:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:501:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:508:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:509:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:508:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:515:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:516:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:515:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:522:14: error: empty paragraph passed to '@param' command [-Werror,-Wdocumentation]
* @param None
~~~~~~~~~~^
ERROR:../../../metal/include/metal/interrupt.h:523:5: error: '@return' command used in a comment that is attached to a function returning void [-Werror,-Wdocumentation]
* @return None
~^~~~~~~~~~~
ERROR:../../../metal/include/metal/interrupt.h:522:11: error: parameter 'None' not found in the function declaration [-Werror,-Wdocumentation]
* @param None
^~~~
ERROR:../../../metal/include/metal/interrupt.h:567:11: error: parameter 'threshold' not found in the function declaration [-Werror,-Wdocumentation]
* @param threshold The interrupt threshold level
^~~~~~~~~
The following code fails to execute when using the Zve* (embedded vector) extensions, because they don't set misa.V:
Lines 255 to 268 in e21891c
(This code should continue to work for the V extension.)
Unfortunately, the RISC-V standard discovery mechanism hasn’t been finalized yet. In the meanwhile, @aswaterman has suggested a workaround that can be used in this case:
This works for both V and Zve*.
Building metal does mtvt
CSR operations, and since that's a CLIC thing it's not part of the upstream GCC port. We should just fall back to a register number if possible, but if we're using all the compiler support it might be better to just fall back to CLINT mode.
See riscv-collab/riscv-gnu-toolchain#423 for more details.
It seems there is not a good platform independent way to determine what drives mtime
and timer interrupts. From examples I've read, it seems people tend to #define
the target board's timebase for mtime
. However, this info does appear to be in the dts files, but just needs to be propagated to an API endpoint. For example, lfrclk
drives mtime
on the HiFive1-RevB, and lfrclk defines it's frequency in the dts.
A new entrypoint metal_cpu_get_mtime_timebase
would be quite useful here to allow platform independent timer behavior.
A potential approach to this could be to have the platform's CPU driver direct this to the applicable subsystem that handles timers (clint0
for FE310), and this can then source the clockbase. Continuing this example on FE310, clint0 then knows that lfrclk
would supply the clock here, and its node in the devicetree could hold a reference to the lfrclk
node to supply the frequency to clint0
Would that scale to other platforms as well?
Around line 293 of src/drivers/sifive_fe310-g000_pll.c:
/* Wait for PLL to lock */
while((__METAL_ACCESS_ONCE(pllcfg) & PLL_LOCK) == 0) ;
Section 6.5 of the FE310-G002 Manual 1p4 says:
The PLL provides a lock signal which is set when the PLL has achieved lock, and which can be
read from the most-significant bit of the pllcfg register. The PLL requires up to 100 μs to
regain lock once enabled, and the lock signal will not necessarily be stable during this initial lock
period so should only be interrogated after this period.
So this code seems to be a little aggressive, not waiting 100uS before entering the wait loop. I have not confirmed why yet, but a test program that configures the PLL to 320MHz infrequently hangs apparently when setting the PLL. Hardware is SparkFun Red-V with R8 corrected to 100 ohms (power supply filter for PLLVDD).
I appreciate a 100uS delay might be hard to arrange here in Metal given it seems to depend on other hardware configuration (LFROSC?) or CPU clock rate.
According to the FE310 Manual the csid register is log2(cs_width)
This implies:
For SPI 0x10024000
Binary | CS |
---|---|
00 | CS0 |
01 | CS1 |
10 | CS2 |
11 | CS3 |
However, in the code https://github.com/sifive/freedom-metal/blob/v201908-branch/src/drivers/sifive_spi0.c#L117 it is set like a cs_width register where each bit corresponds to each chip select line. So the register would act like this
For SPI 0x10024000
Binary | CS |
---|---|
0001 | CS0 |
0010 | CS1 |
0100 | CS2 |
1000 | CS3 |
A fix would be
/* Set CS line */
METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSID) = config->csid;
tabs seem to be 4, but should it be spaces or tabs?
Do you have a well known "named style" to follow?
What thought has been given to c style enforcement?
Would ".clangformat" be useful? it is a bit more "modern" then other solutions.
Reference: https://clang.llvm.org/docs/ClangFormat.html
Reference: https://www.kernel.org/doc/html/latest/process/clang-format.html
Plugins exist for several popular editors
Eclipse: https://marketplace.eclipse.org/content/cppstyle
VIM: https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/clang-format.py
EMACS: https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/clang-format.el
VSCODE: https://marketplace.visualstudio.com/items?itemName=xaver.clang-format
For example some other open packages I've been involved with use this as part of the pull request validation. Effectively, when a pull request is created travis auto-runs a series of tests (including for example format checks) if all goes well - travis votes "approved" auto-magically.
It's controlled by a simple '.travis.yml' file in the root directory.
Example: https://github.com/openthread/openthread/blob/master/.travis.yml
An example of the number of tests and details you can get
https://travis-ci.org/openthread/openthread/builds/541318541?utm_source=github_status&utm_medium=notification
In the file riscv_clint0.c there is a call to __METAL_IO_FENCE(o,i);
on line 188. This macro evaluates to __asm__ volatile ("fence " #pred "," #succ ::: "memory");
. I can not find any explanation what the fence
instruction with two register arguments does and am wondering what the purpose of this statement is.
I'm a little unsure about whether a simuart is meant to be an implementation of a uart or if it is meant to be a distinct device type.
If a simuart is meant to be an implementation of a uart, then it it seems that the metal_uart_get_device()
function does not search through the __metal_simuart_table
for instances of simuarts.
If simuart is meant to be its own device type, then there is no metal_simuart_get_device()
function that allows you to query for instances of simuarts.
On HiFive1 RevB with current Freedom Studio, metal_led_on
turns the LED OFF and metal_led_off
turns the LED ON. GPIOs are being set to the wrong polarity; reversing GPIO states (0,1) in the driver produced the expected behavior:
void __metal_driver_led_on (struct metal_led *led)
{
int pin;
struct metal_gpio *gpio;
pin = __metal_driver_sifive_gpio_led_pin(led);
gpio = __metal_driver_sifive_gpio_led_gpio(led);
if (gpio != NULL) {
metal_gpio_set_pin((struct metal_gpio *) gpio, pin, 0);
}
}
void __metal_driver_led_off (struct metal_led *led)
{
int pin;
struct metal_gpio *gpio;
pin = __metal_driver_sifive_gpio_led_pin(led);
gpio = __metal_driver_sifive_gpio_led_gpio(led);
if (gpio != NULL) {
metal_gpio_set_pin((struct metal_gpio *) gpio, pin, 1);
}
}
I'm trying GCC 10 now. When I use GCC 10 toolchain to build libmetal, there are a lot of ld fail.
/work/melc/riscv64-unknown-elf-gcc-10.0.0-20200203/bin/../lib/gcc/riscv64-unknown-elf/10.0.1/../../../../riscv64-unknown-elf/bin/ld: /scratch/melc/workspace/build/product-coreip-sifive/e76mc/freedom-e-sdk/bsp/e76mc/install/lib/release//libmetal.a(libriscv__mmachine__e76mc_a-tty.o):(.bss.__metal_dt_cpu_3_interrupt_controller+0x0): multiple definition of `__metal_dt_cpu_3_interrupt_controller'; /scratch/melc/workspace/build/product-coreip-sifive/e76mc/freedom-e-sdk/bsp/e76mc/install/lib/release//libmetal.a(libriscv__mmachine__e76mc_a-timer.o):(.bss.__metal_dt_cpu_3_interrupt_controller+0x0): first defined here
After GCC 10, -fno-common is default enable, so that the definitions in metal.h will be placed at .bss. .bss not allow multiple definition.
You can use toolchain /work/melc/riscv64-unknown-elf-gcc-10.0.0-20200203, or GCC 8.3 toolchain with -fno-common option, to reproduce this issue.
It appears that the instruction encoding in the function header blocks in cache.c
are incorrect. For instance:
* @brief CFlush.D.L1 instruction is a custom instruction implemented as a
* state machine in L1 Data Cache (D$) with funct3=0, (for core with data caches)
* It is an I type: .insn i opcode, func3, rd, rs1, simm12(signed immediate 12bs)
* 31 28 27 24 23 20 19 16 15 12 11 8 7 4 3 0
* |--------|--------|--------|--------|--------|--------|--------|--------|
* +-------------+------------+----------+------+--------+-----------------+
* |sign immediate12b (simm12)| rs1 | func3| rd | opcode |
* |-1-1-1-1 -1-1-0-0 -0-0-0-0|-x-x-x-x-x|0-0-0-|-0-0-0-0|-0-1-1-1 -0-0-1-1|
* +--------------------------+----------+------+--------+-----------------+
* 31 -0x40 20 15 0 12 x0 7 0x73 0
Is not matching the RISC-V Spec which shows opcode
as 7 bits and rd
as 5 bits:
This impacts metal_dcache_l1_flush()
, metal_dcache_l1_discard()
and metal_icache_l1_flush()
.
Looking at the code of _metal_trap
, I think it is not interrupt-safe. That is because the code is setting up mepc
and mcause
first, before disabling interrupts by writing to mstatus
. If an interrupt occurs after setting up mepc
and mcause
but before writing to mcause
, then the values in mepc
and mcause
will be overwritten and I think they will not be restored by the interrupt handling code.
By the way: I don't understand the calculation of the value for mepc
. I guess addi t0, ra, -1
is used to rewind mepc
to the instruction in front of the one where execution should continue. But how can applying -1
lead to the correct value, when all RISC-V instructions have to start at an even address and compressed and uncompressed instructions can be mixed?
On FE310 G002 (HiFive RevB), attempting to utilize get_rate_hz on lfrosc is returning 0, but should return 32768 for the external clock.
From tracing, this appears to be because the MUX flag is being read and tested for incorrectly, causing this driver to attempt to read from the internal clock source (which I think is unset, thus resulting in the return value of 0)
The problem appears to be twofold:
Under 9.3 in the SiFive FE310-G002 Manual one can read the following.
mtime is a 64-bit read-write register that contains the number of cycles counted from the rtcclk
input described in Chapter 13. A timer interrupt is pending whenever mtime is greater than or
equal to the value in the mtimecmp register. The timer interrupt is reflected in the mtip bit of the
mip register described in Chapter 8.
On reset, mtime is cleared to zero. The mtimecmp registers are not res
When I take the sifive-welcome example and modify it slightly as displayed below
void wait_for_timer(struct metal_led *which_led) {
// clear global timer isr flag
timer_isr_flag = 0;
// Turn on desired LED
metal_led_on(which_led);
// +++++++ mod begin
printf("t0> %d\n", metal_cpu_get_mtime(cpu0));
// +++++++ mod end
// Set timer
metal_cpu_set_mtimecmp(cpu0, metal_cpu_get_mtime(cpu0) + RTC_FREQ);
// Enable Timer interrupt
metal_interrupt_enable(tmr_intr, tmr_id);
// wait till timer triggers and isr is hit
while (timer_isr_flag == 0){
// +++++++ mod begin
printf("t1> %d\n", metal_cpu_get_mtime(cpu0));
// +++++++ mod end
};
timer_isr_flag = 0;
// Turn off this LED
metal_led_off(which_led);
}
I get the output
t0> 0
t1> 0
t1> 0
...
t1> 0
t0> 0
...
which makes me believe, that the register is not read correctly by the function metal_cpu_get_mtime(), is it?
autoconf_wrapper is taking 5 inputs as arguments from its previous version which was taking 7 arguments.
build.wake is not in sync with the latest changes in the autoconf_wrapper and still providing 7 arguments and the scripts fails.
freedom-metal/src/drivers/riscv_clint0.c
Line 249 in b924ab5
riscv_clint0.c: 249 : __metal_driver_riscv_clint0_command_request()
rc = __METAL_ACCESS_ONCE(
(__metal_io_u32 *)(control_base + (hartid * 4)));
rc = 0;
Hart 0 takes a long time to get to secondary main because it's copying data, zeroing the bss, and running constructors. This makes writing custom secondary_main
s difficult because you have to contend with race conditions on global variables which aren't yet initialized when secondary harts get there.
We need a mechanism for helping users of metal synchronize arrival at user-defined secondary_main
implementations.
We need documentation that describes why Fredom Metal is important, what in means, and how to conceptualize what's going on, beyond the rote API documentation already being generated.
Adding this to Sphinx seems like the best route forward, because it will allow us to expressively create it in reStructuredText with the ability to pull in generated references to the API from Doxygen.
The Sphinx sources can be found in doc/sphinx/
.
If my understanding is correct, the _entry function provided by the metal library sets the stack pointer "sp" to the top (highest memory address) of each core's stack memory. However, the metal_before_start function assumes that the sp is pointing at the lowest address, so it zeros out a different memory than intended.
At https://github.com/sifive/freedom-metal/blob/v201908-branch/src/drivers/sifive_rtc0.c#L31
I believe it should be return metal_clock_set_rate_hz(clock, rate);
https://github.com/sifive/freedom-metal/blob/v201908-branch/src/drivers/sifive_spi0.c#L79
It appears that this
/* Set Polarity */
if(config->polarity) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_PHA_SHIFT);
} else {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_PHA_SHIFT);
}
/* Set Phase */
if(config->phase) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_POL_SHIFT);
} else {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_POL_SHIFT);
}
Should be this
/* Set Polarity */
if(config->polarity) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_POL_SHIFT);
} else {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_POL_SHIFT);
}
/* Set Phase */
if(config->phase) {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_PHA_SHIFT);
} else {
METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_PHA_SHIFT);
}
Our metal_dcache_l1_discard()
contains a fence.i
instruction that is not needed and should be removed:
https://github.com/sifive/freedom-metal/blob/v201908-branch/src/cache.c#L161
Invalidating a line in the data cache or the entire data cache has no impact on instruction side accesses and invalidating the I Cache can have a heavy performance impact.
I was trying to use metal_dcache_l1_flush()
and metal_dcache_l1_discard()
and was taking a trap. While debugging the program, it appears we mishandle the case where we want the entire dcache flushed or discarded. To do the "all" ops, I assume we pass in a 0x0 for the addresss - or how do we indicate the "all" ops?
For the ops that target the entire dcache, we need to use "x0" as the "rs1" operand, not the register holding "address" (which in my testing is picking up register a5). I've added this to the function... no trap is taken but still need to confirm that the cache operation is taking place:
if (!address) {
/* we flush all dcache by using x0 */
__asm__ __volatile__ (".insn i 0x73, 0, x0, x0, -0x40" : : );
__asm__ __volatile__ ("fence.i"); // FENCE
}
else...
File sifive_fe310-g000_pll.c, lines 167-169 are:
/* If we're running off of the PLL, switch off before we start configuring it*/
if((__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL) == 0)
__METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL);
which does not appear to make sense. If PLL_SEL is already 0 (the test in the 'if' statement), there's no point in clearing it. If it is set (unlikely since this is __metal_driver_sifive_fe310_g000_pll_init()) then it will remain set.
Either this needs to be corrected to test for PLL_SEL non-zero or removed.
Hi ~
When testing the big memory region, we found there is a problem in the metal_get_memory_from_address function.
The fail case is as following.
input parameter "address" = 0x8000_0000 and the start address of memory is 0x8000_0000 and the size is 0x8000_0000
The upper_bound value will be 0x8000_0000 + 0x8000_0000 and get a overflow which is not we expected.
14 if ((address >= lower_bound) && (address < upper_bound)) {
15 return mem;
16 }
I have built the freedom gcc toolchain from source. Using this I am trying to build the "hello" program using
make V=1 PROGRAM=hello TARGET=freedom-e310-arty CONFIGURATION=release software
I get the following message:
cd /home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/build/release/ && \
CFLAGS="-march=rv32imac -mabi=ilp32 -mcmodel=medlow -ffunction-sections -fdata-sections -I/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/install/include --specs=nano.specs -DMTIME_RATE_HZ_DEF=32768 -Os" \
/home/mindentropy/freedom-e-sdk/freedom-metal/configure \
--host=riscv64-unknown-elf \
--prefix=/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/install \
--libdir=/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/install/lib/release \
--with-builtin-libgloss \
--with-machine-header=/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/metal.h \
--with-machine-inline=/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/metal-inline.h \
--with-platform-header=/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/metal-platform.h
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for riscv64-unknown-elf-strip... riscv64-unknown-elf-strip
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking whether to enable maintainer-specific portions of Makefiles... no
checking build system type... x86_64-pc-linux-gnu
checking host system type... riscv64-unknown-elf
checking for riscv64-unknown-elf-gcc... riscv64-unknown-elf-gcc
checking whether the C compiler works... no
configure: error: in `/home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/build/release':
configure: error: C compiler cannot create executables
See `config.log' for more details
make: *** [scripts/libmetal.mk:111: /home/mindentropy/freedom-e-sdk/bsp/freedom-e310-arty/build/release/Makefile] Error 77
I have attached my config.log file.
Given that there's no well-defined behavior for 'exit' in an embedded environment, it seems odd that crt0.S includes calls to atexit and exit. I'd suggest removing all of that and having crt0.S cause a trap if main ever returns.
Changing this would allow an embedded RTOS using freedom metal to define it's own _exit function, and we'd be able to build newlib-nano with per-thread atexit handling if desired.
I notice a lot of reference to machine.h but I do not see a file anywhere in the repo with this name. Is there a reason?
I've been trying to get the timer interrupt handler to work, with some partial success. My timer interrupt initialization (seems?) to be correctly initialized, and my interrupt handler does get called (sometimes, mostly once in every 4 execution), however, when on GDB, it seems to be called quite a lot, every once in an ns, but on real execution it does not. Why and what exactly am I doing it wrong?
For some background, I am trying to simulate a context switch mechanism, so ideally I am looking for setting the timer to raise an exception every second, but it seems to be working the opposite of what I am looking for.
typedef struct kctxt_s {
struct metal_cpu *cpu;
struct metal_interrupt *cpu_intr;
struct metal_interrupt *sw_intr;
int sw_intrid;
int cpu_intrid;
int cur_cpu;
struct metal_interrupt *timer_intr;
unsigned long long timebase;
int timer_intrid;
struct metal_pmp *pmp;
struct metal_pmp_config *pmp_config;
struct metal_register_file *regf;
uctxt_t *user_context;
} kctxt_t;
void
_init_timer_intrp(kctxt_t *kctxt)
{
kctxt->timebase = 0;
unsigned long long timeval = metal_cpu_get_timer(kctxt->cpu);
kctxt->timebase = metal_cpu_get_timebase(kctxt->cpu);
if ((timeval == 0) || (kctxt->timebase == 0)) {
log_fatal("[_init_timer_intrp] | Timeval %d timebase %d",
timeval, kctxt->timebase);
return;
}
kctxt->timer_intr = metal_cpu_timer_interrupt_controller(kctxt->cpu);
if (!kctxt->timer_intr) {
log_warn(
"[_init_timer_intrp] | Timer interrupt controller is NULL");
return;
}
metal_interrupt_init(kctxt->timer_intr);
log_info(
"[_init_timer_intrp] | Address of Timer interrupt controller is %p",
kctxt->timer_intr);
kctxt->timer_intrid = metal_cpu_timer_get_interrupt_id(kctxt->cpu);
int rc = metal_interrupt_register_handler(kctxt->timer_intr,
kctxt->timer_intrid, kexcep_timer_handler, kctxt);
if (rc < 0) {
log_fatal(
"[_init_timer_intrp] | Failed. Timer %p interrupt handler %p registration failed",
kctxt->timer_intr, kexcep_timer_handler);
return;
}
log_info(
"[_init_timer_intrp] | Timer interrupt EXCP registered for %p id %d",
kctxt->timer_intr, kctxt->timer_intrid);
// metal_cpu_set_mtimecmp(kctxt->cpu, metal_cpu_get_timebase(kctxt->cpu)
// + RTC_FREQ * 1.5);
metal_cpu_set_mtimecmp(
kctxt->cpu, metal_cpu_get_mtime(kctxt->cpu) + 3 * RTC_FREQ);
metal_interrupt_enable(kctxt->timer_intr, kctxt->timer_intrid);
log_info("[_init_timer_intrp] | Timer interrupt %p enabled for cpu %p",
kctxt->timer_intr, kctxt->cpu);
}
void
kexcep_timer_handler(int id, void *data)
{
kctxt_t *kctxt = (kctxt_t *)data;
metal_interrupt_disable(kctxt->timer_intr, kctxt->timer_intrid);
metal_interrupt_enable(kctxt->timer_intr, kctxt->timer_intrid);
metal_cpu_set_mtimecmp(
kctxt->cpu, metal_cpu_get_mtime(kctxt->cpu) + RTC_FREQ / 2);
log_warn(
"[kexcep_timer_handler] | Exception | Timer interrupt %p | id %d | mtime %ld | timebase %ld",
kctxt->timer_intr, id, metal_cpu_get_mtime(kctxt->cpu),
metal_cpu_get_timebase(kctxt->cpu));
// return_from_excp(kctxt->cpu, id);
}
Since the only way to get a struct mee_cpu *
is to give hartid
to mee_cpu_get()
, the struct mee_cpu
should keep track of which hartid
it references and use that internally during an mee_cpu_get_timer()
call.
Although it doesn't currently appear to be explicitly mandated in any specification (ISA/ABI/etc.), I feel it's the moral responsibility of the C runtime to (re)set vstart = 0
as part of V-extension initialization. Currently there are a few ways the Freedom-Metal user can hack it in, but there are issues with each, and it really seems like the cleanest approach is just making this part of crt0
. It's a one-line addition; I'd plop it right after here.
The __metal_clic0_default_vector_handler
has 64 bytes alignment attribute.
freedom-metal/src/drivers/sifive_clic0.c
Lines 32 to 33 in 26b85c9
clic->metal_mtvt_table[id]
in __metal_driver_sifive_clic0_vector_register
:freedom-metal/src/drivers/sifive_clic0.c
Lines 559 to 590 in e21891c
metal_interrupt_vector_handler_t isr
of __metal_driver_sifive_clic0_vector_register
that is also assigned to clic->metal_mtvt_table[id]
does not have 64 bytes alignment requirement.
So the question is why __metal_clic0_default_vector_handler
needs to be 64 bytes aligned?
Depends on #75 so that we can detect when locks have been allocated in a region of memory which doesn't support atomic operations.
Hey, can anyone help me .I am not able to find the definition for structure metal_spi .
the freedom-e-sdk example-i2c.c fault:
`static void __metal_driver_sifive_i2c0_init(struct metal_i2c *gi2c,
unsigned int baud_rate,
metal_i2c_mode_t mode) {
struct __metal_driver_sifive_gpio0 *pinmux =
__metal_driver_sifive_i2c0_pinmux(gi2c);
struct __metal_driver_sifive_i2c0 *i2c = (void *)gi2c;
if ((pinmux != NULL) && (gi2c != NULL)) {
/* configure I2C I/O pins */
long pinmux_output_selector =
__metal_driver_sifive_i2c0_pinmux_output_selector(gi2c);
long pinmux_source_selector =
__metal_driver_sifive_i2c0_pinmux_source_selector(gi2c);
pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux,
pinmux_output_selector,
pinmux_source_selector);
/* 1: Master 0: Slave */
if (mode == METAL_I2C_MASTER) {
/* Set requested baud rate */
if (metal_i2c_set_baud_rate(gi2c, baud_rate) == METAL_I2C_RET_OK) {
i2c->init_done = METAL_I2C_INIT_OK;
}
} else {
/* Nothing to do. slave mode not supported */
}
}
}`
The hifive-unleashed has no pinmux function, so when did the metal_i2c_init(), it call __metal_driver_sifive_i2c0_init(), and the pinmux = NULL ,so the i2c->init_done = METAL_I2C_INIT_OK will not excute.then when we call __metal_driver_sifive_i2c0_write() and __metal_driver_sifive_i2c0_read(), it will always return METAL_I2C_RET_ERR
In generated BSPs - the function __metal_driver_fixed_clock_rate() should be passed a const clock pointer.
extern inline unsigned long __metal_driver_fixed_clock_rate(const struct metal_clock *clock);
When I compile the example programs I am finding that the output is misaligned. At first I thought it was extra spacing but later realized that the cursor was not going to the beginning of the next line. I am used to unix style line endings but if I change "\n" to "\r\n" then the output of programs looks as I would expect. Am I doing something wrong or should most example programs be updated?
freedom-metal has dependency on newlib, and it should be prevented due to there will be other c library supported in near future.
The UART interrupt interface should be dropped in favor of the general-purpose interface in interrupt.h
Any plans to add I2C support?
according the source code of newlib, the "exit" is a noreturn function ,so it should NOT use a "call" but a "jal" to goto the subprogress
Line 202 in 3abf11d
acctualy the function ”exit“ code emit like this without a "ret" :
Our metal_dcache_l1_flush()
contains a fence.i
instruction that is only needed if one is making code side updates through the data cache - namely pushing out new instructions to memory:
https://github.com/sifive/freedom-metal/blob/v201908-branch/src/cache.c#L135
The fence.i
invalidates the I Cache and can negatively impact performance. If a user is pushing out a code update they should call fence.i
on their own or there should be a new argument added which specifies if the fence.i
should be executed.
Customers don't appreciate having the I Cache invalidated when not necessary.
When I was debugging Krste's vector kernels it was nice that pk (I think) traps illegal instructions and prints the offending opcode and a dump of the x registers to stdout, and then nicely exits.
Once Palmer converted everything to use metal this no longer happened. Instead it simply loops.
This was a problem because it meant that when someone had not correctly updated their toolchain they just got a silent hang.
What happens with a standard elf build with newlib, run with spike pk:
bruce@nuc:~/riscv/bengal-team/software/spike64-kr636_pipe$ spike pk kr636
bbl loader
z 0000000000000000 ra 000000000001021c sp 000000007f7e9ae0 gp 000000000001d5e8
tp 0000000000000000 t0 0000000000010592 t1 000000000001c110 t2 0000000000000000
s0 000000000001bdf8 s1 000000000001e000 a0 000000000000000a a1 000000000001bdf8
a2 000000000001bb28 a3 000000000001d840 a4 0000000000000004 a5 0000000000000048
a6 0000000000000048 a7 0000000000000090 s2 000000000001d000 s3 000000000001c000
s4 0000000000000000 s5 0000000000000000 s6 0000000000000000 s7 0000000000000000
s8 0000000000000000 s9 0000000000000000 sA 0000000000000000 sB 0000000000000000
t3 0000000000000001 t4 000000000001d840 t5 000000000001bb58 t6 000000000001be04
pc 0000000000010392 va 000000005dad0bd7 insn 5dad0bd7 sr 8000000200046020
An illegal instruction was executed!
bruce@nuc:~/riscv/bengal-team/software/spike64-kr636_pipe$
And with metal:
bruce@nuc:~/riscv/bengal-team/software/spike64-kr636_pipe$ spike kr636
or with -l, thousands and thousands of lines of output and then...
bruce@nuc:~/riscv/bengal-team/software/spike64-kr636_pipe$ spike -l kr636
[...]
core 0: 0x000000008000026a (0x5dad0bd7) vmerge.vv v23, v26, v26, v0.t
core 0: exception trap_illegal_instruction, epc 0x000000008000026a
core 0: tval 0x0000000000000000
core 0: 0x0000000080009910 (0x0000a001) c.j pc + 0
When the execution of spike is buried deep within scripts and makefiles it is neither obvious what has happened, nor obvious how to add the -l flag (which you don't otherwise want!) or even that you need it.
(this is a legal RVV instruction, but not yet implemented in spike, which therefore raises illegal instruction)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.