2024-06-13 19:21:56 +00:00
|
|
|
## PicoBlaze Flow / Tutorial
|
|
|
|
|
|
|
|
**THIS SET OF INSTRUCTIONS ASSUMES THE READER IS FAMILIAR WITH THE ISE
|
|
|
|
SUITE FLOW FOR CREATING, IMPLEMENTING AND PROGRAMING PROJECTS.**
|
|
|
|
|
|
|
|
### Overview of Picoblaze / ISE Development flow
|
|
|
|
|
|
|
|
The PicoBlaze development flow is different from the FPGA flow we've
|
|
|
|
seen so far. We have to use a new tool, a PicoBlaze assembler, in
|
|
|
|
conjunction with the ISE tool set.
|
|
|
|
|
|
|
|
<figure>
|
|
|
|
<img src="Picoblaze_flow.png" title="File:Picoblaze_flow.png" />
|
|
|
|
<figcaption><a
|
|
|
|
href="File:Picoblaze_flow.png">File:Picoblaze_flow.png</a></figcaption>
|
|
|
|
</figure>
|
|
|
|
|
|
|
|
I reccomend placing a folder named 'asssembly' or something else
|
|
|
|
memorable in the root of your desired ISE project. This will keep an
|
|
|
|
entire project togetheer in a single directory. You'll want to copy the
|
|
|
|
ROM_form.vhd file from the picoblaze processor distribution to a
|
|
|
|
convenient place, such as ./assembly directory or your project
|
|
|
|
directory. We'll need to have a copy of this to use later.
|
|
|
|
|
|
|
|
The new tool we'll be working with today is openpicide. It is a project
|
|
|
|
management IDE, Syntax check, program assembler and device simulator. It
|
|
|
|
is opensource project, based on the QT framework. We'll be using it to
|
|
|
|
generate the program ROM as a VHDL block ram file, which will be used
|
|
|
|
with our design. The structure of our example project will look like the
|
|
|
|
following:
|
|
|
|
|
|
|
|
1. top_level.v
|
|
|
|
1. embedded_kcpsm3.v
|
|
|
|
1. kcpsm3.v
|
|
|
|
2. prog_rom.vhd - This is the file generated by the assembler.
|
|
|
|
2. project_constraints.ucf
|
|
|
|
|
|
|
|
In general, a picoblaze design would look like this
|
|
|
|
|
|
|
|
1. top_level.v
|
|
|
|
1. embedded_kcpsm3.v
|
|
|
|
1. kcpsm3.v
|
|
|
|
1. prog_rom.vhd - This is the file generated by the
|
|
|
|
assembler.
|
|
|
|
2. periperal1.v
|
|
|
|
3. periperal2.v
|
|
|
|
1. sub_module1.v
|
|
|
|
2. ....
|
|
|
|
4. other logic as needed
|
|
|
|
2. project_constraints.ucf
|
|
|
|
|
|
|
|
## Example \#1
|
|
|
|
|
|
|
|
### Picoblaze simple example / toolchain tutorial
|
|
|
|
|
|
|
|
To begin, make sure you've got Xilinx ISE and OpenpicIDE installed. You
|
|
|
|
can obtain openpicIDE here <http://openpicide.org/> You'll also need to
|
|
|
|
get the picoblaze download package.
|
|
|
|
|
|
|
|
First, we'll create the ISE project. Create a new project called
|
|
|
|
picoblaze_example1 in your projects directory. Add copies of the
|
|
|
|
following files from the picoblaze download package.
|
|
|
|
|
|
|
|
- embedded_kcpsm3.v
|
|
|
|
- kcpsm3.v
|
|
|
|
|
|
|
|
Then create 2 new source files
|
|
|
|
|
|
|
|
- constraints.ucf
|
|
|
|
- picoblaze_example1.v.
|
|
|
|
|
|
|
|
Copy and paste the contents of these files from the wiki \[see below\].
|
|
|
|
You should note that your missing a file, prog_rom, which is in the
|
|
|
|
embedded_kcpsm3 module. Do not attempt to implement the design - it will
|
|
|
|
not work until we generate the program ROM.
|
|
|
|
|
|
|
|
When openpicide opens, you'll want to create a project. You should save
|
|
|
|
the project file in your 'assembly' folder as "picoblaze_example1".
|
|
|
|
Under the settings for the project, you'll need to do the following:
|
|
|
|
|
|
|
|
1. Set the processor to Xilinx picoblaze
|
|
|
|
2. Under the VHDL tab, set the entity name to "prog_rom"
|
|
|
|
3. Set the vhdl source file to the ROM_form.vhd file we copied earlier.
|
|
|
|
4. You can leave the rest of the settings to their default values.
|
|
|
|
|
|
|
|
You'll then need to create a new file - use the new file button in the
|
|
|
|
upper left corner, or go to **File** -\> **New**. Copy and paste the 1st
|
|
|
|
example source file from the wiki. Using the **Picoblaze Menu** Run a
|
|
|
|
syntax check on the code to make sure it is correct, and then generate a
|
|
|
|
VHDL memory file from the code. Save the file as "prog_rom_example1.vhd"
|
|
|
|
in your assembly section. You can now move back to ISE.
|
|
|
|
|
|
|
|
**note: the picoblaze assembly files and the openpicide project file
|
|
|
|
need to be in the same directory**
|
|
|
|
|
|
|
|
Now, add the program file "prog_rom_example1.vhd" to your project, using
|
|
|
|
"Add Copy of Source". You'll notice that the prog_rom module is no
|
|
|
|
longer missing. You now need to comment out line 29, defining the
|
|
|
|
constraint for SW1, from the UCF file. After that is commented out, you
|
|
|
|
can implement the design. After programming the board, you should now
|
|
|
|
see the LED's flipping on and off; if so, you have a working Picoblaze
|
|
|
|
toolchain.
|
|
|
|
|
|
|
|
### constraints.ucf
|
|
|
|
|
|
|
|
This constraints file can be used with the design examples covered in
|
|
|
|
this tutorial.
|
|
|
|
|
|
|
|
#
|
|
|
|
# UCF For Picoblaze Examples
|
|
|
|
#
|
|
|
|
# Period constraint for 50MHz operation, assume a 50% duty cycle, +/- 10%
|
|
|
|
#
|
|
|
|
NET "CLK_50MHZ" PERIOD = 20.0ns HIGH 40%;
|
|
|
|
#
|
|
|
|
# soldered 50MHz Clock.
|
|
|
|
#
|
|
|
|
NET "CLK_50MHZ" LOC = "C9" | IOSTANDARD = LVTTL;
|
|
|
|
|
|
|
|
# Simple LEDs
|
|
|
|
# Require only 3.5mA.
|
|
|
|
#
|
|
|
|
NET "LED<0>" LOC = "F12" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "LED<1>" LOC = "E12" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "LED<2>" LOC = "E11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "LED<3>" LOC = "F11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "led<4>" LOC = "C11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "led<5>" LOC = "D11" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "led<6>" LOC = "E9" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
NET "led<7>" LOC = "F9" | IOSTANDARD = LVTTL | SLEW = SLOW | DRIVE = 4;
|
|
|
|
|
|
|
|
# Simple switches
|
|
|
|
# Pull UP resistors used to stop floating condition during switching.
|
|
|
|
# sw0
|
|
|
|
NET "FPGA_RESET" LOC = "L13" | IOSTANDARD = LVTTL | PULLUP;
|
|
|
|
# sw1
|
|
|
|
NET "SW1" LOC = "L14" | IOSTANDARD = LVTTL | PULLUP;
|
|
|
|
|
|
|
|
### picoblaze_example1.v
|
|
|
|
|
|
|
|
|
|
|
|
module picoblaze_example1(
|
|
|
|
input FPGA_RESET,
|
|
|
|
input CLK_50MHZ,
|
|
|
|
output [7:0] LED
|
|
|
|
);
|
|
|
|
|
|
|
|
wire clock = CLK_50MHZ;
|
|
|
|
// reset is active high.
|
|
|
|
// if no reset signal input
|
|
|
|
// then tie reset to zero here.
|
|
|
|
|
|
|
|
wire reset = FPGA_RESET;
|
|
|
|
wire [7:0] port_id;
|
|
|
|
wire write_strobe;
|
|
|
|
wire read_strobe;
|
|
|
|
wire [7:0] out_port;
|
|
|
|
wire [7:0] in_port=0;
|
|
|
|
wire interrupt=0;
|
|
|
|
wire interrupt_ack;
|
|
|
|
|
|
|
|
|
|
|
|
embedded_kcpsm3 EMBEDDED(
|
|
|
|
.port_id(port_id),
|
|
|
|
.write_strobe(write_strobe),
|
|
|
|
.read_strobe(read_strobe),
|
|
|
|
.out_port(out_port),
|
|
|
|
.in_port(in_port),
|
|
|
|
.interrupt(interrupt),
|
|
|
|
.interrupt_ack(interrupt_ack),
|
|
|
|
.reset(reset),
|
|
|
|
.clk(clock)
|
|
|
|
);
|
|
|
|
|
|
|
|
// only one bit written to by picoblaze, the LED.
|
|
|
|
// therefore don't need to decode port_id.
|
|
|
|
// if write_strobe asserts, grab out_port[0] and
|
|
|
|
// hold it in userbit.
|
|
|
|
|
|
|
|
reg [7:0] userbit = 0;
|
|
|
|
|
|
|
|
always @(posedge clock) begin
|
|
|
|
if(write_strobe) begin
|
|
|
|
userbit <= out_port;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assign LED = userbit;
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
### example \#1 source
|
|
|
|
|
|
|
|
;
|
|
|
|
; simple example code, original
|
|
|
|
;
|
|
|
|
start: LOAD s9, 0xAA
|
|
|
|
drive_wave: OUTPUT s9, 0x02 ; write s9 register to userbit
|
|
|
|
LOAD S2, 0x0F ; S2 initial value
|
|
|
|
loop2: LOAD S1, 0xFF ; S1 initial value
|
|
|
|
loop1: LOAD s0, 0xFF ; S0 initial value
|
|
|
|
loop0: SUB s0, 0x01
|
|
|
|
JUMP NZ, loop0
|
|
|
|
SUB s1, 0x01
|
|
|
|
JUMP NZ, loop1
|
|
|
|
SUB s2, 0x01
|
|
|
|
JUMP NZ, loop2
|
|
|
|
;
|
|
|
|
XOR s9, 0xFF ;toggle register
|
|
|
|
JUMP drive_wave
|
|
|
|
|
|
|
|
## Example \#2
|
|
|
|
|
|
|
|
### Expanding on the example
|
|
|
|
|
|
|
|
Create a new picoblaze project, in the assembly directory. Use the
|
|
|
|
source from example \#2 to create a new program rom file. You can name
|
|
|
|
this program rom "prog_rom_example2.vhd". Copy this into your ISE
|
|
|
|
project, and then remove the original prog_rom_example1.vhd from your
|
|
|
|
project. Rebuild the project and reprogram your board. Your LEDs should
|
|
|
|
be shifty now, instead of inverting!
|
|
|
|
|
|
|
|
### picoblaze_example2.psm source
|
|
|
|
|
|
|
|
;
|
|
|
|
; simple example code, shifting
|
|
|
|
; wait longer too
|
|
|
|
;
|
|
|
|
start: LOAD s9, 0xA5 ; 10100101 ; <---- THIS CHANGED
|
|
|
|
drive_wave: OUTPUT s9, 0x02 ; write s9 register to userbit
|
|
|
|
LOAD S2, 0x2F ; S2 initial value <---- THIS CHANGED
|
|
|
|
loop2: LOAD S1, 0xFF ; S1 initial value
|
|
|
|
loop1: LOAD s0, 0xFF ; S0 initial value
|
|
|
|
loop0: SUB s0, 0x01
|
|
|
|
JUMP NZ, loop0
|
|
|
|
SUB s1, 0x01
|
|
|
|
JUMP NZ, loop1
|
|
|
|
SUB s2, 0x01
|
|
|
|
JUMP NZ, loop2
|
|
|
|
;
|
|
|
|
RL s9 ; shift left register ; <---- THIS CHANGED
|
|
|
|
JUMP drive_wave
|
|
|
|
|
|
|
|
## Example \#3
|
|
|
|
|
|
|
|
### Lets add some input
|
|
|
|
|
|
|
|
Lets read in a switch now! To do this, you can do one of two things. You
|
|
|
|
can add a input port, and add the multiplexed input re g manually. I've
|
|
|
|
already built this example, so you can create a new ISE project. Can
|
|
|
|
call this project "picoblaze_example3". Follow the instructions from
|
|
|
|
earlier in the tutorial, but use the following verilog source file.
|
|
|
|
You'll also need to create a new OpenPicIDE project with the source used
|
|
|
|
in the following example. Make sure that SW1 is not commented out in
|
|
|
|
your UCF file.
|
|
|
|
|
|
|
|
### picoblaze_example3.v source
|
|
|
|
|
|
|
|
module picoblaze_example3(
|
|
|
|
input FPGA_RESET,
|
|
|
|
input CLK_50MHZ,
|
|
|
|
input SW1,
|
|
|
|
output [7:0] LED
|
|
|
|
);
|
|
|
|
|
|
|
|
wire clock = CLK_50MHZ;
|
|
|
|
// reset is active high.
|
|
|
|
// if no reset signal input
|
|
|
|
// then tie reset to zero here.
|
|
|
|
|
|
|
|
wire reset = FPGA_RESET;
|
|
|
|
wire [7:0] port_id;
|
|
|
|
wire write_strobe;
|
|
|
|
wire read_strobe;
|
|
|
|
wire [7:0] out_port;
|
|
|
|
reg [7:0] in_port;
|
|
|
|
wire [7:0] switches;
|
|
|
|
wire interrupt=0;
|
|
|
|
wire interrupt_ack;
|
|
|
|
|
|
|
|
|
|
|
|
embedded_kcpsm3 EMBEDDED(
|
|
|
|
.port_id(port_id),
|
|
|
|
.write_strobe(write_strobe),
|
|
|
|
.read_strobe(read_strobe),
|
|
|
|
.out_port(out_port),
|
|
|
|
.in_port(in_port),
|
|
|
|
.interrupt(interrupt),
|
|
|
|
.interrupt_ack(interrupt_ack),
|
|
|
|
.reset(reset),
|
|
|
|
.clk(clock)
|
|
|
|
);
|
|
|
|
|
|
|
|
// only one bit written to by picoblaze, the LED.
|
|
|
|
// therefore don't need to decode port_id.
|
|
|
|
// if write_strobe asserts, grab out_port[0] and
|
|
|
|
// hold it in userbit.
|
|
|
|
|
|
|
|
reg [7:0] userbit = 0;
|
|
|
|
|
|
|
|
always @(posedge clock)
|
|
|
|
if(write_strobe)
|
|
|
|
userbit <= out_port;
|
|
|
|
|
|
|
|
|
|
|
|
// only one bit is read by picoblaze, the SW1.
|
|
|
|
// therefore don't need to decode port_id.
|
|
|
|
// if read_strobe asserts, grab swib out_port[0] and
|
|
|
|
// hold it in userbit.
|
|
|
|
|
|
|
|
always @(posedge clock)
|
|
|
|
if(read_strobe)
|
|
|
|
in_port <= switches;
|
|
|
|
else
|
|
|
|
in_port <= 8'bX;
|
|
|
|
|
|
|
|
assign LED = userbit;
|
|
|
|
assign switches = {7'b0, SW1};
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
### picoblaze_example3.psm
|
|
|
|
|
|
|
|
;
|
|
|
|
; simple example code, shifting and inverting
|
|
|
|
; wait longer too
|
|
|
|
;
|
|
|
|
; read in a switch at each loop, if the switch is 1 then invert
|
|
|
|
; the register instead of shifting it
|
|
|
|
;
|
|
|
|
;
|
|
|
|
start: LOAD s9, 0xA5 ; 10100101
|
|
|
|
drive_wave: OUTPUT s9, 0x02 ; write s9 register to userbit
|
|
|
|
LOAD S2, 0x2F ; S2 initial value
|
|
|
|
loop2: LOAD S1, 0xFF ; S1 initial value
|
|
|
|
loop1: LOAD s0, 0xFF ; S0 initial value
|
|
|
|
loop0: SUB s0, 0x01
|
|
|
|
JUMP NZ, loop0
|
|
|
|
SUB s1, 0x01
|
|
|
|
JUMP NZ, loop1
|
|
|
|
SUB s2, 0x01
|
|
|
|
JUMP NZ, loop2
|
|
|
|
; Read the inport to s8
|
|
|
|
INPUT s8, 0x01
|
|
|
|
; test bit 0, if 1, set carry flag
|
|
|
|
TEST s8, 0x01
|
|
|
|
; if (s8[0] == 1) invert_wave else shift_wave
|
|
|
|
JUMP C, invert_wave
|
|
|
|
; shift the wave and drive it
|
|
|
|
shift_wave: RL s9 ; shift left register
|
|
|
|
JUMP drive_wave
|
|
|
|
; invert the wave and drive it
|
|
|
|
invert_wave: XOR s9, 0xFF ;toggle register
|
|
|
|
JUMP drive_wave
|
|
|
|
|
|
|
|
However, that code is a bit hard to read!! It can be much easier to read
|
|
|
|
mnemonics instead of hex value and register id's. After getting the code
|
|
|
|
above working, go ahead and add the following to the top of your
|
|
|
|
picoblaze assembly source.
|
|
|
|
|
|
|
|
;
|
|
|
|
; Make it human readable with mnemonics!
|
|
|
|
;
|
|
|
|
|
|
|
|
NAMEREG S0, I_VAR
|
|
|
|
NAMEREG S1, J_VAR
|
|
|
|
NAMEREG S2, K_VAR
|
|
|
|
NAMEREG S9, WAVE_VAR
|
|
|
|
NAMEREG S8, SCRATCH_VAR
|
|
|
|
|
|
|
|
CONSTANT INITIAL_I, 0xFF
|
|
|
|
CONSTANT INITIAL_J, 0xFF
|
|
|
|
CONSTANT INITIAL_K, 0x2F
|
|
|
|
CONSTANT INITIAL_WAVE, 0xA5 ;10100101
|
|
|
|
CONSTANT BITMASK, 0x01
|
|
|
|
;
|
|
|
|
|
|
|
|
After adding this section to your code, go through and replace the
|
|
|
|
following items
|
|
|
|
|
|
|
|
- Register references, sX, should be replaced with the variable names
|
|
|
|
- Constants which are named should be replaced.
|
|
|
|
|
|
|
|
After replacing the constants, you're code should look like this
|
|
|
|
|
|
|
|
start: LOAD WAVE_VAR, INITIAL_WAVE ;
|
|
|
|
drive_wave: OUTPUT WAVE_VAR, 0x02 ; write s9 register to userbit
|
|
|
|
LOAD K_VAR, INITIAL_K ; S2 initial value
|
|
|
|
loop2: LOAD J_VAR, INITIAL_J ; S1 initial value
|
|
|
|
loop1: LOAD I_VAR, INITIAL_I ; S0 initial value
|
|
|
|
loop0: SUB I_VAR, 0x01
|
|
|
|
JUMP NZ, loop0
|
|
|
|
SUB J_VAR, 0x01
|
|
|
|
JUMP NZ, loop1
|
|
|
|
SUB K_VAR, 0x01
|
|
|
|
JUMP NZ, loop2
|
|
|
|
; Read the inport to s8
|
|
|
|
INPUT SCRATCH_VAR , 0x01
|
|
|
|
; test bit 0, if 1, set carry flag
|
|
|
|
TEST SCRATCH_VAR , BITMASK
|
|
|
|
; if (s8[0] == 1) invert_wave else shift_wave\
|
|
|
|
JUMP C, invert_wave
|
|
|
|
; shift the wave and drive it
|
|
|
|
shift_wave: RL WAVE_VAR ; shift left register
|
|
|
|
JUMP drive_wave
|
|
|
|
; invert the wave and drive it
|
|
|
|
invert_wave: XOR WAVE_VAR, 0xFF ;toggle register
|
|
|
|
JUMP drive_wave
|
|
|
|
|
|
|
|
## Run the simulator
|
|
|
|
|
|
|
|
Now we have mnemonics entered into our code, we can easily tweak it.
|
|
|
|
First, we can change the I_Var, J_Var, K_Var, to be smaller values, such
|
|
|
|
as 0x0F. You can then step through the program, or just run it, in the
|
|
|
|
OpenPicIDE simulator.
|
|
|
|
|
|
|
|
## Acknowledgements / References
|
|
|
|
|
|
|
|
This was based on a simple example found here
|
|
|
|
<http://forums.xilinx.com/xlnx/board/message?board.id=PicoBlaze&thread.id=780>
|
|
|
|
The Picoblaze download can be found here
|
|
|
|
<http://www.xilinx.com/products/ipcenter/picoblaze-S3-V2-Pro.htm>
|
|
|
|
OpenPicIDE is available here <http://openpicide.org>
|
|
|
|
|
2024-06-13 01:48:44 +00:00
|
|
|
[Category:FPGAWorkshop](Category:FPGAWorkshop "wikilink")
|