top of page

Soft processors

Aim of the tutorial:

In this tutorial, we will use the LM8 soft processor to display numbers received from UART terminal on a 7-Segment LED display.

Prerequisites:

Previous FPGA tutorials, especially Tut2.

The LM8 soft processor:

There is essentially two way to implement processors in ICs: Hard Core - when the processor is etched in the silicon fabric (used for microcontrollers, DSCs, microprocessors, etc.), and Soft Core - when the core is only a configuration of an FPGA fabric (Proprietary cores such as PicoBlaze, MicroBlaze for Xilinx; NIOS, NIOS II for Intel/Altera; LM8, LM32 for Lattice. There are open source cores such as Amber, BERI, AEMB etc.).

​

Hard Core processors can run at faster clock rates (> 1GHz) because the architecture is optimised, and not bounded by fabric limits. The downside is the fixed architecture - once etched, it can not be modified.

​

Soft Core processors are limited by the speed of the fabric (< 800 MHz). Features can be tuned, added or removed as needed. The core can be multiplied, making it a multi-core processor.

Mico8BlockDiagram.png

The LM8 is an 8bit microcontroller, which is ideal for our MachXO2 FPGA since it will leave a large part of the configurable logic unpopulated for custom IP (uses less than 200 LUTs).

LM8 uses 18b wide instruction set, 32 general purpose registers.

A Wishbone Bus is implemented in the processor, so it is easy to interface the hardened peripherals (SPI, I2C, Timer, UART).

Setting up the processor:

We will use the Lattice Mico Systems program to configure the soft processor.

  • Open Lattice Diamond, and create a project named lm8_led with LCMXO2-1200HC-4SG32C.

  • Open Lattice Mico Systems.

    • In LMS choose LMS perspective and choose File>New Platform.

    • Choose the created folder as a workspace, using LM8 processor, Shared bus arbitration scheme, 25MHz board frequency. Set the FPGA as MachXO2 with all devices, and use a blank platform template.

  • Add the LM8 processor from the CPU category

    • Set 32 general purpose registers.

    • Use the distributed RAM for internal storage.

    • Use a stack depth of 16.

    • Set the PROM size to 2048 (the max amount of assembly instructions).

    • Set the internal Scratch Pad size to 0x800 - this is a small memory zone for fast RW instructions.

Note: We can use external volatile memory for the Scratch Pad, and external non-volatile memory for the ROM.

​

In the MSB perspective, we should see the LM8 Core with the data port and scratch pad. The black line with the outbound arrow indicates a master port in the Wishbone Bus.

​

Add the peripheral components:

The first component will be a GPIO port for the RS, RW, EN control pins of the LCD.

Double click on GPIO under the IO category.

  • Change the name to LED.

  • Change the data width to 4.

  • Change the Wishbone Bus width to 8.

  • Set the port type to outputs only.

​

The second component will be a UART instance with default settings.

​

In the MSB perspective, we should see some blue lines with inbound arrows, which are slave ports.

The master port can initiate, read and write data transaction, while the slave can only reply if the transaction address corresponds.

​

Addresses, priorities, and design rules of the peripherals:

First, we connect the peripherals to the Wishbone Bus by clicking the circles.
We need to generate an address for each peripheral, which can be done with Platform Tools > Generate Address.
Similarly, we must assign priorities for the peripheral interrupts with Platform Tools > Generate IRQ.
The last step is to check the design rules with Platform Tools > Run DRC. If everything is done right, we should have 0 errors.

Platform and Top-Level module:

We can generate the microcontroller platform with Platform Tools > Run Generator.
The generated platform may not be the top-level module in the design, so we need to create the actual top-level module.
Create a new file in platform1/soc folder, name it platform1_top.v, copy the following Verilog content, and import into Diamond.

Here platform1.v contains the architecture of the LM8 created above. We have the UART input, and output and the Segment control signal outputs.

 

We set the internal oscillator to 24.18MHz to provide the clock signal for the processor.

​

We also create an instance of the BCD 2 Segment logic from the previous tutorial.

​

Finally, we create an instance of the processor, and we connect every required IO.

​

​

Software application code:

Next, we will create the software application by using C/C++ in Lattice MicoSystem Software Project Environment. This code will run on the processor.

Let's switch to the C/C++ perspective in LMS

  • File > New > Mico Managed Make C Project.

  • Choose <...>/platform1/ as the project content location.

  • Choose <...>/platform1/soc/platform1.msb as the target hardware MSB system.

  • Choose the UART test project template.

​

The UART test program creates an UART echo function, and an LED cycling function. We will change the LED function.

​

First, let's change the displayed message. The original function used the '0' as the escape character, but we want to display the numbers 0 ... 9, so we will use 'x' as the escape character, and the message will look like:

Lastly, we will change the content of the infinite loop in the SM_Toggle function: If we receive a numeric character, send the numeric value (hex) to the BCD decoder.

Writing to the GPIO is done with the MICO_GPIO_WRITE_DATA function, which expects a 32b value. for our 4b wide GPIO we can use the MICO_GPIO_WRITE_DATA_BYTE0 macro as shown below:

Final tasks:

  1. We need to build our C program with the Project > Build Project option. The compiler generates an .elf (executable linked format) file, which contains the LM8 instructions and the pre-initialized data.

  2. We need to deploy the program to the LM8 by using the Tools > Software Deployment option. Here we create a new launch configuration specifying the previously generated .elf file. This will generate the required PROM and Scratch Pad memory files.

  3. We load the generated memory files into the LM8. In LMS perspective double-click on the processor, and add the initialisation files (prom_init.mem, scratchpad_init.mem) for the PROM and Scratch Pad.

  4. Generate the processor Verilog module with the included memory units by using Platform Tools > Run Generate.

  5. Configure the Diamond environment: Tools > Options > General - clear the option "Copy file to Implementation's Source directory when adding existing file". We don't want to copy the source files, only to reference them. This way, any change in the sources is automatically loaded at implementation.

  6. Synthesise the design.

  7. Add the pin configuration in text mode (.lpf) or spreadsheet view.

  8. Map design

  9. Place and Route

  10. Generate JEDEC file

  11. Upload the generated file with the programmer.

  12. If everything went according to plan we should be greeted by a simple text menu in a terminal program, and we have the option to use a UART echo module, or to display numbers as the the video shows below.
     

As always, the necessary files can be found here.

bottom of page