FPGAWorkshop17Notes: Difference between revisions
From HacDC Wiki
No edit summary |
|||
Line 58: | Line 58: | ||
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. | 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 file need to be in the same directory''' | '''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. | 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. |
Revision as of 15:34, 9 March 2010
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.
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:
- top_level.v
- embedded_kcpsm3.v
- kcpsm3.v
- prog_rom.vhd - This is the file generated by the assembler.
- embedded_kcpsm3.v
- project_constraints.ucf
In general, a picoblaze design would look like this
- top_level.v
- embedded_kcpsm3.v
- kcpsm3.v
- prog_rom.vhd - This is the file generated by the assembler.
- kcpsm3.v
- periperal1.v
- periperal2.v
- sub_module1.v
- ....
- other logic as needed
- embedded_kcpsm3.v
- 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:
- Set the processor to Xilinx picoblaze
- Under the VHDL tab, set the entity name to "prog_rom"
- Set the vhdl source file to the ROM_form.vhd file we copied earlier.
- 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