Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement register file to block RAM manually? #30

Closed
rgwan opened this issue Feb 9, 2017 · 5 comments

Comments

Projects
None yet
3 participants
@rgwan
Copy link

commented Feb 9, 2017

My toolchain doesn't support to implement register file as dedicated memory. How to implement it manually to reduce core size?

@cliffordwolf

This comment has been minimized.

Copy link
Owner

commented Feb 9, 2017

Hmm. This is a strange limitation. What architecture/toolchain is that?

I see two options:

Option 1: Use Yosys to synthesize the pivorv32 verilog code into lower-level verilog with the register file mapped to a block ram. I.e. do something like this:

$ cat picorv32_presyn.ys 
read_verilog picorv32.v
chparam -set COMPRESSED_ISA 1 picorv32
prep -top picorv32
memory_bram -rules picorv32_regs.txt
write_verilog -noattr picorv32_presyn.v

$ cat picorv32_regs.txt 
bram picorv32_regs
  init 0
  abits 5
  dbits 32
  groups 2
  ports  2 1
  wrmode 0 1
  enable 0 1
  transp 0 0
  clocks 1 1
  clkpol 1 1
endbram

match picorv32_regs
  make_transp
endmatch

$ yosys picorv32_presyn.ys 

This will create a picorv32_presyn.v file with a "pre-synthesized" version of the core that contains an instance of a picorv32_regs module:

  picorv32_regs \cpuregs.0.0.0  (
    .A1ADDR(_0008_),
    .A1DATA(_0140_),
    .A2ADDR(_0009_),
    .A2DATA(_0141_),
    .B1ADDR(latched_rd),
    .B1DATA(cpuregs_wrdata),
    .B1EN(_0000_[31]),
    .CLK1(clk)
  );

You have to provide an implementation of this module, which can be a simple wrapper for your block rams. A1 and A2 are the read ports, B1 is the write port.

The following (untested) code demonstrates the semantics of the picorv32_regs interface:

module picorv32_regs (
    input [4:0] A1ADDR, A2ADDR, B1ADDR,
    output reg [31:0] A1DATA, A2DATA,
    input [31:0] B1DATA,
    input B1EN, CLK1
);
    reg [31:0] memory [0:31];
    always @(posedge CLK1) begin
        A1DATA <= memory[A1ADDR];
        A2DATA <= memory[A2ADDR];
        if (B1EN) memory[B1ADDR] <= B1DATA;
    end
endmodule

If you enable interrupts with Q registers then you'll have to change abits 5 to abits 6 in picorv32_regs.txt and you'll have to provide a block ram wrapper with a 6-bit wide address port.

If you set ENABLE_REGS_DUALPORT=0 then you should also change the ports line in picorv32_regs.txt to ports 1 1 and you will get an instance of a block RAM with only one read port.

Option 2: Refactor the picorv32.v code to work around the limitations of the tool chain you are using.

This is the relevant code from picorv32.v for reading from and writing to the register file:

    always @(posedge clk) begin
            if (resetn && cpuregs_write)
                    cpuregs[latched_rd] <= cpuregs_wrdata;
    end

    always @* begin
            decoded_rs = 'bx;
            if (ENABLE_REGS_DUALPORT) begin
                    cpuregs_rs1 = decoded_rs1 ? cpuregs[decoded_rs1] : 0;
                    cpuregs_rs2 = decoded_rs2 ? cpuregs[decoded_rs2] : 0;
            end else begin
                    decoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1;
                    cpuregs_rs1 = decoded_rs ? cpuregs[decoded_rs] : 0;
                    cpuregs_rs2 = cpuregs_rs1;
            end
    end

Note that this code does not contain any read FFs. If you only have block RAMS with read FFs then manually refactoring the code to use them explicitly might be hard and option 1 becomes much more interesting.

@rgwan

This comment has been minimized.

Copy link
Author

commented Feb 9, 2017

It is Anlogic AL3, and compile by it's proprietary tool TD.

@rgwan

This comment has been minimized.

Copy link
Author

commented Feb 9, 2017

@cliffordwolf
I try to use yosys for pre-synth, but when I run demo, it gives me not correct result:

rgwan@laptop:~/anlogic/picorv32_demo$ vvp tb
VCD info: dumpfile system.vcd opened for output.

Hellb Jbeld! If lbh caa eead ghif meffage ghea
ghe CicbEI32 CCH feemf gb be jbekiag jhfg fiae.

            GEFG CAFFED!

rgwan@laptop:~/anlogic/picorv32$ make
iverilog -o testbench.vvp -DCOMPRESSED_ISA -DRISCV_FORMAL testbench.v picorv32_presyn.v bram_register.v
testbench.v:147: warning: parameter COMPRESSED_ISA not found in testbench.top.uut.
testbench.v:150: warning: parameter ENABLE_DIV not found in testbench.top.uut.
testbench.v:151: warning: parameter ENABLE_IRQ not found in testbench.top.uut.
testbench.v:149: warning: parameter ENABLE_MUL not found in testbench.top.uut.
testbench.v:152: warning: parameter ENABLE_TRACE not found in testbench.top.uut.
chmod -x testbench.vvp
vvp -N testbench.vvp
TRAP after 7 clock cycles
ERROR!
Makefile:14: recipe for target 'test' failed
make: *** [test] Error 1

(BTW, these options I have enabled in origin file)

@cliffordwolf cliffordwolf reopened this Feb 9, 2017

@cliffordwolf

This comment has been minimized.

Copy link
Owner

commented Feb 9, 2017

(I've fixed a typo in my verilog code above: A1DATA, A2DATA are of course [31:0], not [4:0]. But I don't think this is the issue you are having.)

I've now added a complete example for this:
https://github.com/cliffordwolf/picorv32/tree/master/scripts/presyn

Seems to be working fine here:

picorv32/scripts/presyn$ make
yosys -v0 picorv32_presyn.ys
End of script. Logfile hash: 230c775540
CPU: user 1.44s system 0.02s, MEM: 56.36 MB total, 24.29 MB resident
Yosys 0.7+97 (git sha1 8480620, clang 3.8.0-2ubuntu4 -fPIC -Os)
Time spent: 14% 6x opt_expr (0 sec), 13% 6x opt_clean (0 sec), ...
iverilog -o testbench.vvp testbench.v picorv32_presyn.v
/opt/riscv32ic/bin/riscv32-unknown-elf-gcc -Os -ffreestanding -nostdlib -o firmware.elf firmware.S firmware.c \
	 --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug -lgcc
/opt/riscv32ic/bin/riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin
python3 ../../firmware/makehex.py firmware.bin 4096 > firmware.hex
vvp -N testbench.vvp
VCD info: dumpfile testbench.vcd opened for output.

Hello World! If you can read this message then
the PicoRV32 CPU seems to be working just fine.

                TEST PASSED!

TRAP.
@gsteiert

This comment has been minimized.

Copy link

commented Mar 1, 2019

The register code listed above:

module picorv32_regs (
    input [4:0] A1ADDR, A2ADDR, B1ADDR,
    output reg [31:0] A1DATA, A2DATA,
    input [31:0] B1DATA,
    input B1EN, CLK1
);
    reg [31:0] memory [0:31];
    always @(posedge CLK1) begin
        A1DATA <= memory[A1ADDR];
        A2DATA <= memory[A2ADDR];
        if (B1EN) memory[B1ADDR] <= B1DATA;
    end
endmodule

That shows the read data is registered so it shows up after the rising edge of CLK1.
The latest version in picorv32 is asynchronous without the register.
Is there a way to get the registered version to work again?
Some block ram implementations are clocked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.