GithubHelp home page GithubHelp logo

sifive / freedom-metal Goto Github PK

View Code? Open in Web Editor NEW
152.0 66.0 47.0 16.13 MB

Bare Metal Compatibility Library for the Freedom Platform

License: Other

Makefile 1.01% M4 0.57% Shell 0.20% C 94.84% Assembly 3.34% Dockerfile 0.04%

freedom-metal's Introduction

Freedom Metal Machine Compatibility Library

Documentation for the Freedom Metal library can be found here.

freedom-metal's People

Contributors

albertchen-sifive avatar benmezger avatar bradseevers avatar briangraysonsiv avatar bsousi5 avatar daveparry avatar davidstwchen avatar dconn-sifive avatar e-puerto avatar ericlove avatar issuehsu avatar jaxonwht avatar jerryshih avatar keith-packard avatar kito-cheng avatar lesderid avatar loiclefort avatar lsix avatar mmicko avatar nandkumarjoshi avatar nategraff-sifive avatar nick-knight avatar palmer-dabbelt avatar paul-walmsley-sifive avatar reclusejack avatar resilinix avatar richardxia avatar sifive-eblot avatar sifivekevin avatar zongbox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

freedom-metal's Issues

LED polarity might be inverted

@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.

embedded documentation is not up to date

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
          ^~~~~~~~~

Vector unit fails to initialize for Zve* targets

The following code fails to execute when using the Zve* (embedded vector) extensions, because they don't set misa.V:

freedom-metal/gloss/crt0.S

Lines 255 to 268 in e21891c

/* Check for vector extension support and enable it if found.
* Omit if toolchain doesn't support the vector extension. */
#ifdef __riscv_v
csrr a5, misa
li a4, 0x200000
and a5, a5, a4
beqz a5, 1f
csrr a5, mstatus
ori a5, a5, 0x200
csrw mstatus, a5
vsetivli x0, 0, e8, m1, ta, ma
csrwi vcsr, 0
1:
#endif

(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:

  1. Unconditionally initialize mstatus.VS to Dirty. This is safe on any processor.
  2. Read back mstatus.VS and see if it’s set to Off. If so, there is no vector unit, and you should skip initialization.

This works for both V and Zve*.

Metal requires a SiFive Toolchain

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.

API for accessing `mtime` base clock

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?

configure_pll() might be in a rush to test PLL_LOCK

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.

SPI chip select set incorrectly

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;

Conventions used in metal - questions

  1. Whitespace & Tabs - what is the style here?

tabs seem to be 4, but should it be spaces or tabs?

  1. Do you have a well known "named style" to follow?

  2. 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

  1. Do you have a travis or similar automation system in place tied to github pull requests?

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

fence instruction with two arguments

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.

No API for getting a simuart device instance?

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.

metal_led_off/on set wrong LED state

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);
    }
}

Multiple definitions in libmetal.a

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.

Instruction encoding in the comments field for metal_cache functions are incorrect

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:

image

This impacts metal_dcache_l1_flush(), metal_dcache_l1_discard() and metal_icache_l1_flush().

Is `_metal_trap` interrupt-safe?

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?

__metal_driver_sifive_fe310_g000_lfrosc_get_rate_hz returns wrong clock

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:

  1. The mux/cfg reg values read from the devicetree are read as raw addresses and not as offsets into the base address for the aon block
  2. The MUX flag (AON 0x7C:31) is low for selecting the external clock (it's high in lfextclksel, or 0x7C:0), but the logic appears to be testing for a high bit in bit 31

Possible bug in metal_cpu_get_mtime()

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?

build.make is not on Sync up with scripts/autoconf_wrapper

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.

Synchronize arrival at secondary_main

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_mains 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.

Document the why of Freedom Metal

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/.

metal_before_start wrongly initializing stack memory

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.

SPI polarity and phase bit shifts swapped

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);
    }

metal_dcache routines not accounting for "all" operations

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... 

__metal_driver_sifive_fe310_g000_pll_init() incorrectly tests PLL_SEL

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.

There is a problem in metal_get_memory_from_address API

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 }

Build fails with freedom gnu toolchain

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.

Why does freedom-metal have any 'exit' handling?

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.

Where is machine.h

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?

How to handle timer interrupts

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);
}

V-extension: should initialize vstart

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.

Why __metal_clic0_default_vector_handler needs to be 64 bytes aligned?

The __metal_clic0_default_vector_handler has 64 bytes alignment attribute.

void __metal_clic0_default_vector_handler(void)
__attribute__((interrupt, aligned(64)));

it is assigned to clic->metal_mtvt_table[id] in __metal_driver_sifive_clic0_vector_register :
int __metal_driver_sifive_clic0_vector_register(
struct metal_interrupt *controller, int id,
metal_interrupt_vector_handler_t isr, void *priv) {
int rc = -1;
struct __metal_driver_sifive_clic0 *clic =
(struct __metal_driver_sifive_clic0 *)(controller);
// struct metal_interrupt *intc =
// __metal_driver_sifive_clic0_interrupt_parent(controller);
int num_subinterrupts =
__metal_driver_sifive_clic0_num_subinterrupts(controller);
metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic);
if ((mode != METAL_SELECTIVE_VECTOR_MODE) &&
(mode != METAL_HARDWARE_VECTOR_MODE)) {
return rc;
}
if ((mode == METAL_SELECTIVE_VECTOR_MODE) &&
(__metal_clic0_interrupt_is_vectored(clic, id) == 0)) {
return rc;
}
if (id < num_subinterrupts) {
if (isr) {
clic->metal_mtvt_table[id] = isr;
clic->metal_exint_table[id].exint_data = priv;
} else {
clic->metal_mtvt_table[id] = __metal_clic0_default_vector_handler;
clic->metal_exint_table[id].sub_int = priv;
}
rc = 0;
}
return rc;
}

But the parameter 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?

Add atomic locks

Depends on #75 so that we can detect when locks have been allocated in a region of memory which doesn't support atomic operations.

definition missing

Hey, can anyone help me .I am not able to find the definition for structure metal_spi .

sifive_i2c0.c for hifive-unleashed

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

missing const parameter in generated BSPs.

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);

Carriage returns required for alignment?

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?

Default output of sisense-welcome
Screenshot_2021-06-17_00-13-46

After swapping "\r\n" in for "\n"
Screenshot_2021-06-17_00-15-27

metal_dcache_l1_flush() should not issue fence.i by default

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.

metal silently loops after illegal instruction

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.