Why another Hardware Processor?

Hardware description languages are good for particular problems (e.g parallel memory calculation jobs). But it is tough to handle sequential execution problems with them. For such problems the sequential execution of a programm often fits well. A standard solution would be to use a Microcontroller Core (e.g. from Opencores written in VHDL). To programm this Microcontroller often the C programming language is used. The C code is converted to Assembler and the Assembler code is then loaded into the Microcontroller. To accomplish such a task you at least need a C programming environment and a VHDL programming enviroment and you need a chain between them create and load the Assembler. What if you could write your Microcontroller Code in the same environment where you describe your hardware. This is where the Python Hardware Processor together with Myhdl fits in. So the idea was to create a Python Hardware Processor that supports just enougth of the python language to handle sequential execution problems. Parallel array processing jobs and comparable, are then implemented in the hardware description language where they normaly nicely fit and perform well.

How Code and Data is mapped in the pyCPU

Download pyCPU

You can get the curent development files at:

http://sourceforge.net/projects/pycpu/files/

Explaning the Files on sourceforge:

RS232_Norbo.py …..Implements the RS232 Receiver/Transmitter Unit

MakeProcessorBytecode.py …..Generates the Code for the Cpu from the Python code.

pyCPU.py …..Implennts the  Complete CPU and uses RS232_Norbo.py and MakeProcessorBytecode.py for this

Using the UART unit of the pyCPU

The pyCPU has a UART unit now.

The Baudrate of the UART unit is set in the Myhdl code. The unit does a 8 bit transmission with no parity and one Stopbit. The usage of the now tightly  integrated UART unit  is demonstrated in the code below:

PREDEFINED_IO_ADDRESSES={'PORTA_IN':0,'PORTB_IN':1,'PORTC_OUT':2,'PORTD_OUT':3, \
'RS232Data':4,'RS232_READ_ADDRESS':5,'RS232_RECEIVE_ADDRESS':6,'RS232_WRITEBUFFER_FULL':7 } 

def CPU_main():
  global PORTA_IN,PORTB_IN,PORTC_OUT,PORTD_OUT,RS232Data,RS232_READ_ADDRESS,RS232_RECEIVE_ADDRESS,RS232_WRITEBUFFER_FULL
  x=0
  RS232_READ_ADDRESS=0
  while 1:
    ##### RS232 Transmitting ######
    # Only write something to the Transmit buffer if not full
    if not RS232_WRITEBUFFER_FULL:
      # Write the value of x to the RS232 Transmit 
      # buffer if something is in the Transmit-Buffer, sending
      # is started automatically
      RS232Data=x    
      x=x+1

    ##### RS232 Receiving ######
    # Wait until a byte is received in the Receive-Buffer
    if RS232_READ_ADDRESS!=RS232_RECEIVE_ADDRESS:  
      # Read data from RS232 receive buffer at the RS232_READ_ADDRESS
      PORTD_OUT=RS232Data    
      # Set the Read address the the current received
      RS232_READ_ADDRESS=RS232_RECEIVE_ADDRESS

Advantages:

  • Easier access to RS232 than in most Microcontrollers today
  • Receive-/ and Transmit-Buffer (Buffersize is set in the hardware description)

Disadvantages:

  •  No interrupt
  • No dynamic baudrate configuration
  • Only 8 Bit Mode, no parity bit and 1 Stopbit
  • If you want more options you have to upgrade the hardware description

Supported Python Code

  • Int’s (with … bits, depends on how many you what to instanciate on the hardware), no other python types only plane ints (it means no list,dict,tuple,etc..)
  • If and while (comparisions with <,>, >=, ⇐,!=,== )
  • Assignments to globals or local varibles from const, globals or defined local variables
  • Operators: +. -, ^ ,~, |, & ,«,»
  • Simple Digital IOs access for Hardware PORTS (implemented through a distinct global varible)
  • Function calls (with: consts, globals, or local vars as argument and one intager as return value), no default arguments, no keyword arguments,no functions or functioncalls as arguments
  • nested while loops up to CONST_LOOP_DEPTH (Constant default value = 4)
  • up to MAX_NUMBER_FUNCTION_ARGUMENTS (Constant default value = 10) arguments
  • up to CONST_PC_STACK_DEPTH (Constant default value=5) number of nested function calls
  • up to VAR_MEM_DEPTH (Constant default value=20) local variables

Simple pyCPU Programcode example

The Following is a simple program for the pyCPU (Python Hardware Processor). Keep in mind that the Databitwitdth is limited to a constant factor given to the Myhdl description.

PREDEFINED_IO_ADDRESSES={'PORTA_IN':0,'PORTB_IN':1,'PORTC_OUT':2,'PORTD_OUT':3 } 

def busywait(time):
  x=0
  while x<100:
    td=0
    x=x+1
    while td<100:
      td=td+1
      ss=0
      while ss<time:
	ss=ss+1

def CPU_main():
  global PORTA_IN,PORTB_IN,PORTC_OUT,PORTD_OUT
  x=0
  while 1:
    PORTD_OUT=PORTD_OUT^16
    busywait(20)
    if (PORTB_IN & 0x01)==1:
      PORTD_OUT=PORTD_OUT|0x20
    else:
      PORTD_OUT=PORTD_OUT&0xdf

To get a little insight you can use the dis python module to show the bytecode instructions. The instructions for the function CPU_main are shown below .

import dis
dis.dis(CPU_main)
  3           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (x)

  4           6 SETUP_LOOP              62 (to 71)

  5     >>    9 LOAD_GLOBAL              0 (PORTD_OUT)
             12 LOAD_CONST               2 (16)
             15 BINARY_XOR          
             16 STORE_GLOBAL             0 (PORTD_OUT)

  6          19 LOAD_GLOBAL              1 (busywait)
             22 LOAD_CONST               3 (20)
             25 CALL_FUNCTION            1
             28 POP_TOP             

  7          29 LOAD_GLOBAL              2 (PORTB_IN)
             32 LOAD_CONST               4 (1)
             35 BINARY_AND          
             36 LOAD_CONST               4 (1)
             39 COMPARE_OP               2 (==)
             42 POP_JUMP_IF_FALSE       58

  8          45 LOAD_GLOBAL              0 (PORTD_OUT)
             48 LOAD_CONST               5 (32)
             51 BINARY_OR           
             52 STORE_GLOBAL             0 (PORTD_OUT)
             55 JUMP_ABSOLUTE            9

 10     >>   58 LOAD_GLOBAL              0 (PORTD_OUT)
             61 LOAD_CONST               6 (223)
             64 BINARY_AND          
             65 STORE_GLOBAL             0 (PORTD_OUT)
             68 JUMP_ABSOLUTE            9
        >>   71 LOAD_CONST               0 (None)
             74 RETURN_VALUE

Python Hardware Processor -> pyCPU Intro

The Python Hardware Processsor  is a implementation of a Hardware CPU in Myhdl. The CPU can directly execute something very similar to python bytecode (but only a very restricted instruction set). The Programm code for the CPU can be written directly in python (very restricted parts of python). This code is then converted by a small python programm to this restricted python bytecode. Since the hardware description is also in python, the slightly modified bytecode an then automatically loaded into the CPU design.

Most “bytecode” instructions are executed in the Hardware CPU with one instruction per cycle. If you have enought hardware available you can simply instantiate more cores on an FPGA with different Programmcode to make them run in parallel.

Due to the python nature of Myhdl and the Python Hardware Prozessor written with it, it
allows you, to write a Programm for the prozessor, to simulate the Hardware Processor
and to convert the Processor to a valid hardware description (VHDL or Verilog) inside
a single python environment.