PicoBlaze - Program RAM Access for an Interactive Monitor
I have a confession to make: I love PicoBlaze! There are many reasons to love it. It is a tiny CPU (96 Spartan3 slices or 26 Spartan6 slices plus a BRAM). It is simple. It is bug-free. It's pretty fast. It can reduce the size and the complexity of your design - instead of debugging a big state machine, just throw one (or more) of these in. Add a serial output and you can debug your fpga from inside out!
However, there are a few problems. The instruction set is not too enjoyable. If you want C, you can't have it (I can live with that...There are some attempts, I am told). The tools for linux are not great. In particular, it doesn't seem to be possible to use the JTAG loader in linux (am I wrong? Has anyone done it?), and I do not like Windows. That means I have to assemble, run data2mem and reconfigure my design every time I change Pico code. There seems to be no way to load code into an existing design (in linux anyway).
Another thing I miss is an interactive monitor running over the serial port. I like interactive debugging. Unfortunately, you can't read or write to the BRAM, so you can't dump code or write a loader, making existing attempts at monitors less useful.
Let's fix the memory writing problem first.
Hardware to read and write BRAM.
Since the PicoBlaze uses a single port of a BRAM, we can use the other port for reading and writing via an IO-addressed device. PicoBlaze is an 8-bit processor, but the address bus is 10 bits wide. The instruction bus is even worse- its a whopping 18 bits wide.
While we could address the second BRAM port as 8-bit memory, we would lose the 2 remaining bits ( they are in parity bits available in 9-, 18- and 36-bit modes). 9-bit addressing gets too confusing - the weird ninth bit and the addresses being renumbered in a strange way. Since the goal is to see what's in the Pico's program memory, let's just stick with 18 bits.
The most straightforward solution is to implement 3x 8-bit ports for data, and 2x 8-bit ports for the address, capable of reading and writing. The address will be held in an 18-bit wide flip-flop array. Since incrementing is pretty much free in hardware, we may as well dedicate a port to the incrementor and save all the adds with carry in software.
Changes to the PicoBlaze Program ROM
The Pico is normally connected to a single-ported BRAM. A dual-ported one is required here, so let's fix that first.
Where is program memory instantiated? There is a file called ROM_form.v (or its vhdl equivalent elsewhere) that is used as a template to generate the BRAM instance, by the assembler. It needs to be changed to something dual-ported like this:
RAMB16_S18_S18 ram_1024_x_18(
.DIA (16'h0000),
.DIPA (2'b00),
.ENA (1'b1),
.WEA (1'b0),
.SSRA (1'b0),
.CLKA (clk),
.ADDRA (address),
.DOA (instruction[15:0]),
.DOPA (instruction[17:16]),
// adding 2nd port for monitor
.DIB (mtoram[15:0]),
.DIPB (mtoram[17:16]),
.ENB (1'b1),
.WEB (mwr),
.SSRB (1'b0),
.CLKB (clk),
.ADDRB (maddr),
.DOB (mfromram[15:0]),
.DOPB (mfromram[17:16])
);
Of course, we need to connect the new PORTB memory ports to the outside, so the module declaration in the template file is expanded accordingly:
module {name} (address, instruction, clk,
maddr,mfromram,mtoram,mwr);
Changes to the top module
The instance of PicoBlaze now looks like this in our top module:
embedded_kcpsm3 cpu(
port_id,
write_strobe,
read_strobe,
out_port,
in_port,
interrupt, interrupt_ack,
0, //not using reset
clk,
// monitor ram
maddr[9:0],
mfromram[17:0],
mtoram[17:0],
mwr
)
Now we need to decode the IO space, which depends on what else you have in your PicoBlaze system. I only have a serial port at locations 0 and 1, allowing for a quick and dirty solution:
// IO input mux
always @ (*)
case(port_id[3:0])
4'h0: in_port = {3'b000,rx_data_present, rx_full, rx_half_full, tx_full, tx_half_full};
4'h1: in_port = rx_data[7:0];
4'h2: in_port = maddr[9:8];
4'h3: in_port = maddr[7:0];
4'h4: in_port = mfromram[17:16];
4'h5: in_port = mfromram[15:8];
4'h6: in_port = mfromram[7:0];
default: in_port = 8'h12;
endcase
//output:
always @(posedge clk)
if(write_strobe)
case(port_id[2:0])
//address ports
3'h2: maddr[9:8] ‹= out_port;
3'h3: maddr[7:0] ‹= out_port;
//data ports
3'h4: mtoram[17:16] ‹= out_port;
3'h5: mtoram[15:8] ‹= out_port;
3'h6: mtoram[7:0] ‹= out_port;
//incrementor
3'h7: maddr ‹= maddr + 1;
endcase
//
always @(*) begin
if(write_strobe)
case(port_id[2:0])
3'h4: mwr=1;
3'h5: mwr=1;
3'h6: mwr=1;
default: mwr=0;
endcase
end
It's sloppy but does the job. If someone wishes to make a clean module, please opensource it and let me know!
A Quick Test
CONSTANT STATUS,0
CONSTANT UART,1
CONSTANT MADDR_HI,2
CONSTANT MADDR_LO,3
CONSTANT MRAM_HI,4
CONSTANT MRAM_ME,5
CONSTANT MRAM_LO,6
CONSTANT MADDR_INC,7
;load address 0
load s4,00
output s4,MADDR_HI
load s4,00
output s4,MADDR_LO
;read it back and display the address
input s4,MADDR_HI
call hex2
input s4,MADDR_LO
call hex2
call sp
;
; read the data MADDR and display it...
input s4,MRAM_HI
call hex2
input s4,MRAM_ME
call hex2
input s4,MRAM_LO
call hex2
call cr
hex2 outputs a 2-digit hex number in s4 to my serial port; cr and sp are carriage return and space, respectively.
My serial port terminal shows
0000 000400
According to documentation, 000400 is in fact LOAD S4,00.
Similar tests show that writing and incrementing works too.
Now what?
The Picoblaze now has access to its own program ROM, that is RAM... It is now capable of running a monitor, modifying its own code and loading code via serial port. These are all good things.
At a future date I will try to tackle a small monitor program for the PicoBlaze. Unless you beat me to it - in which case, please share your tools and solutions with all of us.
Xilinx and Spartan3 and PicoBlaze are registered trademarks of Xilinx Inc.
- Comments
- Write a Comment Select to add a comment
there are new tools for Picoblaze development. Take a look at
http://www.moravia-microsystems.com/multitarget-development-system/
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: