# PicoRV32
## 安裝 Gnu toolchain
```bash
git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
git submodule update --init --recursive
```
### Prerequisites
On Ubuntu, executing the following command should suffice:
```bash=
sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build
```
### Installation (Linux)
To build the Linux cross-compiler, pick an install path. If you choose, say, /opt/riscv, then add /opt/riscv/bin to your PATH now.
```bash=
vim ~/.bashrc
export RISCV="/opt/riscv"
export PATH=$PATH:$RISCV/bin
:wq #save and quit
source ~/.bashrc
```
Create a directory in opt
```bash=
cd /opt
sudo mkdir riscv
sudo chown $USER riscv
```
Then, simply run the following command:
```bash=
cd riscv-gnu-toolchain
./configure --prefix=/opt/riscv --with-arch=rv32gc --with-abi=ilp32d
make linux
```
### Result
```bash=
make[12]: Nothing to be done for 'install-data-am'.
make[12]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib/import'
make[11]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib/import'
make[10]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib/import'
make[9]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib/import'
make[8]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib'
make[7]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib'
make[6]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb/build-gnulib'
make[5]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb'
make[4]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb'
make[3]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib/gdb'
make[2]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib'
make[1]: Leaving directory '/home/eason/riscv-gnu-toolchain-rv32i/build/build-gdb-newlib'
mkdir -p stamps/ && touch stamps/build-gdb-newlib
```
:::success
Gnu-toolchain install complete.
:::
## 安裝PicoRv32
:::danger
~~Not complete riscv32i build tools~~
Not complete make test_verilator
:::
### install
```bash=
make download-tools
make -j$(nproc) build-tools
```
### Problem
* riscv32i build tools
:::warning
```bash=
make[4]: Leaving directory '/home/eason/picorv32/riscv-gnu-toolchain-riscv32i/build/build-gcc-newlib-stage1/gcc'
make[3]: Leaving directory '/home/eason/picorv32/riscv-gnu-toolchain-riscv32i/build/build-gcc-newlib-stage1'
mkdir -p stamps/ && touch stamps/build-gcc-newlib-stage1
make[2]: Leaving directory '/home/eason/picorv32/riscv-gnu-toolchain-riscv32i/build'
make[1]: *** [Makefile:158: build-riscv32i-tools-bh] Error 2
make[1]: Leaving directory '/home/eason/picorv32'
make: *** [Makefile:167: build-tools] Error 2
```
:::
* I found there are others have the same problem:
https://github.com/YosysHQ/picorv32/issues/213
:::success
* Modify the code line :
picorv32/Makefile :
```bash=150
git submodule update --init $$$$reference_riscv_gcc riscv-gcc; \
git submodule update --init $$$$reference_riscv_glibc riscv-glibc; \
git submodule update --init $$$$reference_riscv_newlib riscv-newlib; \
mkdir build; cd build; ../configure --with-arch=$(2) --prefix=$(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1)) #; make
```
picorv32/riscv-gnu-toolchain-rv32i/riscv-gdb/gdb/Makefile.in :
```bash=427
unittests/scoped_restore-selftests.c \
unittests/string_view-selftests.c \ (delete it !)
unittests/tracepoint-selftests.c \
unittests/unpack-selftests.c \
```
Result :
```bash=
+ ../configure --with-arch=rv32i --prefix=/opt/riscv32i
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for grep that handles long lines and -e... /usr/bin/grep
checking for fgrep... /usr/bin/grep -F
checking for grep that handles long lines and -e... (cached) /usr/bin/grep
checking for bash... /bin/bash
checking for __gmpz_init in -lgmp... yes
checking for mpfr_init in -lmpfr... yes
checking for mpc_init2 in -lmpc... yes
checking for curl... /usr/bin/curl
checking for wget... /usr/bin/wget
checking for ftp... /usr/bin/ftp
configure: creating ./config.status
config.status: creating Makefile
config.status: creating scripts/wrapper/awk/awk
config.status: creating scripts/wrapper/sed/sed
make[1]: Leaving directory '/home/eason/picorv32'
```
:::
* make test_verilator
::: warning
```bash=
eason@eason-virtual-machine:~/picorv32$ make test
/opt/riscv32i/bin/riscv32-unknown-elf-gcc -Os -mabi=ilp32 -march=rv32imc -ffreestanding -nostdlib -o firmware/firmware.elf \
-Wl,--build-id=none,-Bstatic,-T,firmware/sections.lds,-Map,firmware/firmware.map,--strip-debug \
firmware/start.o firmware/irq.o firmware/print.o firmware/hello.o firmware/sieve.o firmware/multest.o firmware/stats.o tests/addi.o tests/add.o tests/andi.o tests/and.o tests/auipc.o tests/beq.o tests/bge.o tests/bgeu.o tests/blt.o tests/bltu.o tests/bne.o tests/div.o tests/divu.o tests/jalr.o tests/jal.o tests/j.o tests/lb.o tests/lbu.o tests/lh.o tests/lhu.o tests/lui.o tests/lw.o tests/mulh.o tests/mulhsu.o tests/mulhu.o tests/mul.o tests/ori.o tests/or.o tests/rem.o tests/remu.o tests/sb.o tests/sh.o tests/simple.o tests/slli.o tests/sll.o tests/slti.o tests/slt.o tests/srai.o tests/sra.o tests/srli.o tests/srl.o tests/sub.o tests/sw.o tests/xori.o tests/xor.o -lgcc
bash: line 1: /opt/riscv32i/bin/riscv32-unknown-elf-gcc: No such file or directory
make: *** [Makefile:110: firmware/firmware.elf] Error 127
eason@eason-virtual-machine:~/picorv32$ riscv32-unknown-elf-gcc
riscv32-unknown-elf-gcc: command not found
```

Under the path , i didnt find riscv32-unknown-elf-gcc
:::
### How to solve
I can't find the answer.
## pico.v
### modify the code to add the new instruction.
* I want to implement power function.
* which is `power rd,rs1,rs2`
* It can calculat rs1^rs2 and store it back to rd.
|31:25|24:20|19:15|14:12|11:7|6:0|
|:-:|:-:|:-:|:-:|:-:|:-:|
|funct7|rs2|rs1|funct3|rs|opcode|
```verilog=644
// Instruction Decoder
reg instr_power; // add the new instruction
reg instr_lui, instr_auipc, instr_jal, instr_jalr;
reg instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu;
```
```verilog=680
assign instr_trap = (CATCH_ILLINSN || WITH_PCPI) && !{instr_lui, instr_auipc, instr_jal, instr_jalr,
instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu,
instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw,
instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai,
instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and,
instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, instr_fence,
instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer,
instr_power}; // add this
```
```verilog=737
if (instr_power) new_ascii_instr = "power"; // add this
if (instr_add) new_ascii_instr = "add";
if (instr_sub) new_ascii_instr = "sub";
if (instr_sll) new_ascii_instr = "sll";
```
```verilog=863
is_lui_auipc_jal_jalr_addi_add_sub <= |{instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub, instr_power}; // add this
```
* And next I need to modify the code that is used to sepcify the `opcode`.

* I choose `opcode = 0011011` that there is no one using this `opcode`
* Remember to add the `opcode` to `is_alu_reg_imm`
```verilog=877
is_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011;
is_lb_lh_lw_lbu_lhu <= mem_rdata_latched[6:0] == 7'b0000011;
is_sb_sh_sw <= mem_rdata_latched[6:0] == 7'b0100011;
is_alu_reg_imm <= mem_rdata_latched[6:0] == 7'b0010011 | mem_rdata_latched[6:0] == 7'b0110011;
is_alu_reg_reg <= mem_rdata_latched[6:0] == 7'b0110011;
```
```verilog=1151
instr_addi <= 0;
instr_slti <= 0;
instr_sltiu <= 0;
instr_xori <= 0;
instr_ori <= 0;
instr_andi <= 0; // add this
instr_power <= 0;
```
* Add the `alu_out`
```verilog= 1274
case (1'b1)
is_lui_auipc_jal_jalr_addi_add_sub:
alu_out = alu_add_sub;
instr_power :
alu_out = alu_power_out;
```
* Add the calculate.
```verilog=1230
reg [31:0] alu_add_sub;
generate if (TWO_CYCLE_ALU) begin
always @(posedge clk) begin
alu_add_sub <= instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2;
alu_power_out <= reg_op1 ** reg_op2;
alu_eq <= reg_op1 == reg_op2; // add this calculate
alu_lts <= $signed(reg_op1) < $signed(reg_op2);
alu_ltu <= reg_op1 < reg_op2;
alu_shl <= reg_op1 << reg_op2[4:0];
alu_shr <= $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0];
end
end else begin
always @* begin
alu_add_sub = instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2;
alu_power_out <= reg_op1 ** reg_op2;
alu_eq = reg_op1 == reg_op2; // add this calculate
alu_lts = $signed(reg_op1) < $signed(reg_op2);
alu_ltu = reg_op1 < reg_op2;
alu_shl = reg_op1 << reg_op2[4:0];
alu_shr = $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0];
end
end endgenerate
```
* We can modify the code to save logical resources and power consumption.
```verilog=
alu_power_out <= (reg_op1 % 2) ? reg_op1 << ((reg_op1 >> 1)*reg_op2) : reg_op1 ^ reg_op2;
```
* Because if use the the Multiplier logical resources and power consumption will be high.
* So I choose to first determine whether reg_op1 is a power of 2, and then decide whether to use the left shift operator or the multiplication operator.