Actions

FPGAWorkshop17Notes

From HacDC Wiki

Revision as of 21:05, 6 March 2010 by Williamgibb (talk | contribs)

PicoBlaze Flow Instructions

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

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.

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, and you can now 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 

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!

example #2 source

;
; simple example code, shifting
; wait longer too
;
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
;
RL s9 ; shift left register
JUMP drive_wave

Lets have 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.

example #3 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;
                end
        end

        // 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


example #3 source

;
; 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 


Acknowledgements

This was based on a simple example found here