FPGAWorkshop17Notes

From HacDC Wiki
Jump to: navigation, search

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.

Picoblaze flow.png

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