Opbasm provides -m to do the same as well as -s to limit the scratchpad memory size to 64 or bytes. MEM format files are output by default. Local labels do not have to be globally unique, making it easier to construct commonly used names inside procedures without concern for collisions or excessively long names. Internally, local labels are implemented by appending them to the immediately preceeding global label. The fully expanded name can be referred to anywhere in the program.
|Published (Last):||14 March 2010|
|PDF File Size:||6.41 Mb|
|ePub File Size:||15.23 Mb|
|Price:||Free* [*Free Regsitration Required]|
Otherwise the next instruction is executed. Notice that the false block appears before the true block. If an instruction you already need to use changes the flags in a useful way then you can check them directly without a compare.
Consider you are incementing a register and want to detect when it overflows past 0xFF. In this case the result is zero so you could compare for equality with 0x00 but the add instruction also sets the C flag on overflow so you could also just branch directly after the increment. The most fundamental of these are the while loop and do-while loop which only differ in when the loop expression is evaluated: either before or after the block.
This is done by creating a subroutine. These are blocks of code that begin with a label like those used for jump targets. You enter into the subroutine with a call instruction. It will branch to the target label just like jump but it also saves the next address on to the hardware call stack.
When the subroutine is finished the return instruction pops the most recent address from the stack and resumes execution after the call instruction that initiated the jump into the subroutine.
If you jump into a subroutine and then execute return you will pop the wrong address from the call stack and have a malfunction. Likewise, you must not allow the processor to enter into a subroutine by normal sequential execution without a call to prepare the hardware stack. Any subroutines placed before the main program must be skipped over with a jump instruction. Be extremely careful when writing recursive subroutines that call themselves. The call instruction has a conditional form that works the same as a conditional jump.
It allows you to use a subroutine as the body of a control structure. Higher level languages employ a calling convention where they save registers not deemed as temporaries onto a stack at the beginning of a subroutine and restore these saved values before returning.
With this approach you can reuse the same register for different purposes in a program. The stack is a region of memory that expands as more data is pushed onto it and shrinks as data is popped off. Most processors have special instructions to assist in managing such a stack in RAM but not the PicoBlaze. The hardware call stack is dedicated to storing only return addresses and is unavailable for general purpose use.
It is possible, however, to create a stack in the scratchpad memory and emulate the behavior of push and pop operations. To accomplish this we reserve a register to function as a stack pointer. It will hold an index into scratchpad memory that always points to the next free location on the stack. Pushes and pops will manipulate the pointer and move data to and from the scratchpad memory.
You must pop registers in the reverse of the order they were pushed to restore them to their original state. It would be disastrous if the stack register were mistakenly changed by another part of your code while the stack is in use. After which, register sF is no longer accessible by its default name. Any code trying to use it by the old name will fail to assemble. In most cases the stack is designed to grow down from higher addresses to lower addresses. Typically you would place the stack at the upper end of the scratchpad and use the lower end for other purposes.
It is important that the stack never grows large enough to overwrite other data stored in scratchpad. While simple in concept, this can all get a bit tedious and clutter your code. The Opbasm macro library has push and pop macros as well as other stack handling macros to simplify stack management when writing your programs.
With a stack in place you can use it to enforce a calling convention for your subroutines. Within a subroutine, all modified registers must be saved to the stack before modification unless they are designated as temporaries that are never saved or a return value. When this convention is followed a subroutine caller never sees registers change before and after a call except the return value register.
That can grow to a significant portion of the total program memory if the stack is used extensively across many subroutines. Since it is implemented as a soft-core within an FPGA there will usually be additional logic outside of the PicoBlaze that you need to interact with.
There are input and output ports which are multiplexed together onto an 8-bit address bus. These ports are accessed with the input and output instructions. Refer to the official PicoBlaze documentation for examples of how this can be done. If you want to completely decode the ports and save their state in registers, one general purpose solution would be to instantiate the generic register file component from the VHDL-extras library.
It will be up to you to decide how the ports are applied to control your external logic. Some may be used to transfer data values in and out of the picoblaze. Others can be used to set control flags or initiate actions in external state machines. This is a mechanism where external hardware can interrupt normal program execution to cause special code known as an interrupt service routine ISR to run.
Interrupts are optional. You do not have to use them in your designs. Interrupt handling is controlled by an internal flag. Interrupts are off by default. The enable instruction will enable the interrupts. The disable instruction disables them. When the interrupt input goes high the PicoBlaze saves the current instruction address on the hardware stack like a normal subroutine call.
It also saves the values of the Z, C flags, and on PB6, saves the active register bank. The processor then executes the instruction located at the interrupt vector address. You must exit from the ISR using the returni instruction instead of return. You can choose whether to re-enable them with the returni instruction or later on with an enable instruction outside the ISR.
The returni resumes execution at the address saved upon the start of the interrupt. The saved Z, C, and register bank are restored to their previous values. Execution can then proceed as normal. You must be careful not to let the ISR disrupt processor state such that execution fails after resuming normal execution. An easy but inconvenient solution is to reserve some registers for exclusive use by the ISR. Otherwise you must implement a stack as described above and push all registers that will be modified before changing them.
Similarly, the ISR should only modify scratchpad locations it has exclusive write access to so as to avoid corrupting the normal program in progress. For this reason it is best to minimize the amount of code in the ISR to minimize its execution time. The VHDL-extras library includes a general purpose interrupt controller that can help with servicing multiple interrupts.
PicoBlaze 8-bit Microcontroller