Actions

FPGAWorkshop17Notes

From HacDC Wiki

Revision as of 02:42, 24 November 2010 by Yrewevaca (talk | contribs)

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.

<pre>

  1. UCF For Picoblaze Examples
  2. Period constraint for 50MHz operation, assume a 50% duty cycle, +/- 10%

NET "CLK_50MHZ" PERIOD = 20.0ns HIGH 40%;

  1. soldered 50MHz Clock.

NET "CLK_50MHZ" LOC = "C9" | IOSTANDARD = LVTTL;

  1. Simple LEDs
  2. 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;

  1. Simple switches
  2. Pull UP resistors used to stop floating condition during switching.
  3. sw0

NET "FPGA_RESET" LOC = "L13" | IOSTANDARD = LVTTL | PULLUP;

  1. sw1

NET "SW1" LOC = "L14" | IOSTANDARD = LVTTL | PULLUP; </pre>

picoblaze_example1.v

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

example #1 source

<pre>

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

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

<pre>

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

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

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

picoblaze_example3.psm

<pre>

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

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. <pre>

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

</pre>

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

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

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 <br> The Picoblaze download can be found here http://www.xilinx.com/products/ipcenter/picoblaze-S3-V2-Pro.htm <br> OpenPicIDE is available here http://openpicide.org <br>