Skip to content

yuchangyuan/run_c_snippet_on_picorv32

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Run C snippet on pico RISC-V core with simulator

warm up

what we want

  • C code snippet
int main()
{
  putStrLn("Hello, world!");
}
  • run on RISC-V core, in software way

what we have

  • gcc riscv32 toolchain, for cross compile
  • iverilog, for simulate verilog hardware description
  • picorv32, a 32bit RISC-V core, in verilog source form

how to

top-down

  • reverse to ordinary condition, mostly
  • design hardware to make software run easily

step

  • complete trival C code to a full project
  • design hardware peripheral, according to C project
  • wrap up & run the code, with simulator
  • more experiment

software side(C code)

analysis: what code snippet actually do

add place-holder function to make code compile

./sw/s2.c

compile with RISC-V cross compiler, then disassemble

  • riscv32-unknown-elf-gcc -nostdlib -Os -o s2 s2.c
  • riscv32-unknown-elf-objdump -D s2 > s2.list

list file

./sw/s2.list

actual do

  • memory access
    • fetch instruction
    • read data
    • write data
  • IO access
    • out_char (currently a place holder)

convert to hardware requirement

  • we have core
  • we need memory
  • we need output port(no need of input)

program, memory & hardware

what a C program contain

  • read only data
  • readable & writable data
    • need initialize
    • uninitialized or initialize to 0

section

  • text
  • data(sdata/rodata)
  • bss(COMMON)

memory layout design for hardware

in order to run the program:

  • we need to “place” our program in hardware, to be access by CPU;
  • we call the hardware device as “memory”.

hardware spec & restriction

  • start address of core, at 0x0000
  • both addres range of any memory block should continuous
  • capacity of memory better be power of 2, and width is 32
  • we have two type of memory: ROM & RAM

hardware requirement

  • place read only data into ROM, or RAM(we don’t need to write)
  • initialize data in RAM, for data need initialize

common case for reference

small ROM & RAM
  • tiny ROM place at address 0, act as bootloader
  • code in bootloader access IO device, load actual program from external to RAM
    • include text, data section
    • not include bss/COMMON section
  • jump to RAM, and run the program
flash & RAM
  • flash place at address 0, read only part of a program
  • flash can be programed
  • a copy of readable & writable data also in flash
    • copied to RAM before run actual program
    • data section

initialize code

both case include some code to initialize or prepare for environment before actual program run

our choice

  • similar to “flash & RAM”
    • we can recreate ROM when program change, like program a flash memory
  • ROM at address 0, size 128K, contain the text section
  • ROM at address 128K, size 128K, contain a copy of data section
  • RAM at address 256K, size 256K
    typenamestart addrsize
    ROMrom00128Ki
    ROMrom1128Ki128Ki
    RAMram256Ki256Ki

alternative choice

  • do not include rom1, but a special RAM can be initialized
    • no need of initialize code
    • need special RAM implementation or RAM
    • need extra hardware to initialize the RAM, this may contain other ROMs
  • do not include rom0 & rom1, but a special RAM can be initialized
    • same as above
  • merge rom0 & rom1 into a single ROM, and include a table in ROM, which contain the each section address and size
    • more complex initialize code
    • more complex to generate the ROM

program, IO & hardware

  • use some signal to indicate we output a ‘char’
  • access like a memory(memory mapped)
    • a write to special address indicate an output operation
    • the data written to the address is the ‘char’ to output

our choice

use address 512KiB as special address

so we can update function out_char, see ./sw/s3.c

some alternative

we can also use address 0, which is not usually writable, as the special address

linker script

  • program do not determine how to place itself in memory;
  • place by linker(called by compiler in link stage);
  • a “linker script” is used control linker;

how to place our program

rom0

  • code start at address 0x0
    • actual entry for a program is not “main”
    • some code need to run before “main”
    • usually the entry is called “_start”
  • all RO data place in rom0
  • text section place in rom0

rom1

  • not use after program start

ram

  • place RW data need to initialize place at beginning
  • then place RW data need to initialize to 0
  • then other data
  • stack place at the end

actual linker script

./sw/s3.lds

see: info:ld#Scripts for format ref: ./picorv32/picosoc/sections.lds

initial code

  • set stack reg
  • copy data from rom1(.data section) to ram
  • initialize necessary memory range to 0(.bss section)
  • call main

see:

hardware side(verilog HDL)

ram design

./hw/ram.svg

OPaddressinput dataoutput data
WRITE(word)0x13x-
WRITE(low 16bit)0x55y(low 16bit)-
WRITE(high 16bit)0xFFz(high 16bit)-
READ0x13-x
READ0x22-w
READ0x33-u

./hw/ram.v

rom design

./hw/rom.svg

addressvalue
A1D1
A2D2
A3D3
A4D4
A5D5

top design

./picorv32/README.md

./hw/mem.svg

OPaddressinput dataoutput data
WRITE(word)ax-
WRITE(high byte)by(high 8bit)-
READc-u
READd-v
READe-w

./hw/top.v

testbench

./hw/tb.v

wrap up

./hw/gen.sh

About

A step by step guide to run trival C snippet on picorv32 core with simulator

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published