The previous article introduced the ARM DesignStart program, which mentioned the Cortex-M1/M3 DesignStart FPGA version, supporting Xilinx and domestic Gowin platforms. This article will guide you through how to build an Cortex-M3 soft core processor on FPGA based on the ARM DesignStart program, using the Xilinx Artix-7™ series FPGA as an example. It will cover how to customize an ARM Cortex-M3 SoC soft core, add GPIO and UART peripherals, develop applications in the Keil MDK environment, download and debug ARM programs using Jlink, and the final implementation effect is LED blinking and serial output of Hello World information.
What Content is Included?
-
Necessary Basic Knowledge
-
Cortex-M3 FPGA IP Core Download
-
Hardware Preparation
-
Software Preparation
-
Cortex-M3 Soft Core Construction
-
Create a Vivado Project
-
Add IP Core Search Path
-
Create Block Design
-
Add IP Cores, GPIO, and UART Peripherals
-
SWD Interface Connection
-
Peripheral Base Address Allocation
-
Pin Assignment
-
Generate Bitstream File for Download
-
Cortex-M3 Soft Core Program Design
-
Create a Keil Project
-
Implement GPIO Input and Output Control
-
Implement Serial Data Sending and Receiving
-
Implement Delay Function
-
Generate Flash Programming Algorithm
-
Download and Run
-
Open Source Address
-
Reference Materials
This article is quite long, and I have compiled it into a PDF file. The created Vivado project, ARM Cortex-M3 IP core, Keil-MDK project, Flash programming algorithm files, and other materials have been uploaded to GitHub and Gitee, and the open source address can be found at the end of the article.
Alternatively, follow my public account and reply in the background: 220327 to get the download link for the materials.
1.Necessary Basic Knowledge
To quickly complete the implementation of the ARM Cortex-M3 soft core on FPGA, some necessary basic knowledge is required!
-
Basic knowledge of FPGA development, such as FPGA development process, design, synthesis, layout, routing, constraints, downloading
-
Basic use of Xilinx Vivado development environment, such as Block Design design method, pin assignment, bitstream file generation and downloading
-
Basic usage of ARM Cortex-M3 core, such as development of STM32, MM32, GD32, CH32 and other microcontrollers.
-
Basic use of Keil-MDK development environment, basic project establishment, compilation, download process.
If you have the above knowledge, congratulations! You can complete the implementation of the ARM Cortex-M3 soft core on FPGA within 2 hours.
2.Cortex-M3 FPGA IP Core Download
First, we need to obtain the ARM Cortex-M3 FPGA soft core IP package from the ARM official website.
The download address is as follows:
https://silver.arm.com/browse/AT426
File Name: Cortex-M3 DesignStart FPGA-Xilinx edition(r0p1-00rel0)
File Size: 7.52MB
MD5SUM: cd67536c29023429cde47130d51b6f49
Downloading from the official website requires account registration. If the download speed is very slow, you can reply in the public account background: 220318 to obtain the download link and copy it to the browser for download.
After unzipping the compressed package, there are a total of 4 folders:
Contents stored in each folder:
-
docs
Contains ARM Cortex-M3 processor reference manual, DesignStart FPGA version usage instructions, top-level Block Design diagram based on Arty-A7 development board, etc.
-
hardware
Contains Vivado project based on Digilent Arty-A7 development board, top-level Block Design files, pin constraint files, Testbench files, etc.
-
software
Contains Keil-MDK project, SPI Flash programming algorithm files, etc.
-
vivado
Includes DesignStart Cortex-M3 Xilinx FPGA version IP core files, where the Arm_ipi_repository folder is the core source files. The contents of the IP files are encrypted and not readable.
3.Hardware Preparation
To complete the DS CM3 setup on FPGA, we need at least the following hardware:
-
A piece of Artix-7™ development board to build the Cortex-M3 soft core SoC. I am using the Zedboard Pro Development Board, with FPGA model XC7A100T.
-
Xilinx FPGA downloader for downloading the soft core bitstream to FPGA, such as Platform USB Cable, JTAG-HS2/HS3, etc.
-
ARM Cortex-M3 debugger for debugging ARM core programs, such as JlinkV9, Jlink-OB, etc.
The official DS CM3 IP core is based on Digilent’s Arty-A7 development board, with FPGA models XC7A35T/100T, and the recommended Vivado version is v2019.1. If you happen to have this development board, you can directly use the sample project provided by the official.
Digilent Arty-A7 development board:
Zedboard Pro Development Board:
4.Software Preparation
-
Xilinx Vivado development environment, the recommended version is 2018.2 or higher. I am using version 2018.3.
-
Keil MDK development environment, such as version 5.33.
-
DS_CM3’s Keil device package.
Download the device support package specifically for DesignStart Cortex-M3 from the Keil official website, download link is as follows:
https://keilpack.azureedge.net/pack/Keil.V2M-MPS2_DSx_BSP.1.1.0.pack
5.Cortex-M3 Soft Core Setup
With the above hardware and software prepared, we can start the setup of the Cortex-M3 soft core.
First, create a folder named cortex_m3_on_xc7a100t to store all the project files for this example, and create the following folders:
Functions of each folder:
-
bd folder
Used to store Block Design designs.
-
cm3_core folder
Used to store ARM Cortex-M3 core IP core files.
-
doc folder
Used to store design documents.
-
flash folder
Used to store generated bit and mcs files.
-
rtl folder
Used to store user-designed Verilog source files.
-
xdc folder
Used to store pin and timing constraint files.
The cm3_core folder needs to copy the Arm_ipi_repository folder from the official compressed file, with the path AT426-BU-98000-r0p1-00rel0
.
evado">Arm_ipi_repository
After preparing the above folders, we can start creating the project.
5.1 Create a Vivado Project
Open Vivado 2018.3, open the project creation wizard, enter the project name, and the project storage path is the folder we created earlier.
Select the complete model of the FPGA chip: XC7A100TFGG484.
After the project is successfully created, the project directory will look like this:
5.2 Add IP Core Search Path
To be able to search for the ARM Cortex-M3 processor IP core in Block Design, we need to add the path where the ARM soft core IP is located to the search path.
5.3 Create Block Design
To facilitate the subsequent graphical connection of various IP cores, we use the Block Design graphical design method, which allows for the quick construction of a customized soft core processor.
Create a Block Design, name it cm3_core, and save it in the bd folder created initially.
Add the Cortex-M3 processor core to the canvas:
Double-click the Cortex-M3 IP core to perform some basic configurations. We do not need the Trace function, select No Trace, use SWD interface for debugging, and disable the JTAG port:
Set the instruction space and data space size to 64KB, and do not initialize them.
5.4 Add Some Necessary IP Cores
-
Clock PLL
Used to provide clock signals to the core, bus, and peripherals. Here we configure it to 50MHz single-ended input, and the PLL output is set to 50MHz. If the clock frequency is set higher, synthesis will prompt WNS, TNS timing not met, which may affect the normal operation of the system.
-
Processor Reset IP
Used to provide the reset signal required by the core, peripherals, and interconnect components. No customization is needed, keep the default settings.
-
Bus Interconnect IP
The Cortex-M3 core uses the AHB bus, which has been converted to the AXI3 bus internally. The GPIO/UART and other peripheral IP cores provided by Xilinx are AXI4-Lite buses, so we need to add a bus interconnect matrix to convert different protocols, setting the number of slaves to 1 and the number of masters to 2, connected to the SYS bus of the processor.
-
Basic Logic Gate IP
The Cortex-M3 core requires a low-level reset, while the reset IP outputs a high-level reset, so we need to insert a NOT gate in between to convert it.
-
Constant IP
This soft core construction does not involve the interrupt part, so both IRQ and NMI are set to constant 0. If interrupts need to be connected to the processor, multiple interrupt sources can be merged into one using the Concat core to connect to IRQ.
Add the above IPs to the Block Design canvas and connect them as shown below:
According to the official manual, the ARM soft core IP already includes ITCM and DTCM memory, so we do not need to add external BRAM for program and data storage.
The ITCM and DTCM provided in the core are both based on RAM, which means that when we use Keil to download the program, it is only downloaded to RAM, and the data will be lost when power is off.
At this point, the ARM Cortex-M3 processor core has been set up. Next, we will add GPIO and UART peripherals.
5.5 Add GPIO and UART Peripherals
Some commonly used microcontrollers, such as STM32, generally have a fixed number of TIM, UART, SPI, CAN, etc. peripherals inside the chip. However, when we use FPGA to build an ARM soft core SoC, the configuration is more flexible. If you do not need SPI, you do not need to add SPI peripherals. If you need 10 UARTs, you can add 10 UARTs. The configuration of peripherals is relatively flexible, but of course, these peripherals are implemented based on FPGA logic resources, and the actual number added will be limited by the size of the FPGA chip’s logic resources.
Below, we will take adding a set of AXI GPIO and a set of AXI UART as examples to introduce how to use the ARM soft core to control these two peripherals.
The AXI GPIO peripheral provided by Xilinx has the following features:
-
There are two channels, channel 1 and channel 2, each channel supports up to 32 pins
-
Each pin can be configured as input or output mode
-
Each pin can set a reset initial value
-
Supports interrupt output
The AXI UART peripheral provided has the following features:
-
Full duplex
-
Supports 5-8 data bits
-
Supports parity check
-
Configurable baud rate from 110 to 230400
Here we will configure GPIO as a dual-channel, channel 1 as output mode, with the low 4 bits connected to LEDs, and channel 2 as input mode, with the low 4 bits connected to buttons.
UART is configured to a baud rate of 115200, with 8 data bits and no parity check.
After the configuration is complete, connect them to the host interface of the interconnect IP:
The clocks for these two IPs can use the same clock as the processor, and the reset can use the reset signal output from the reset IP.
For detailed usage of AXI GPIO and AXI UART, please refer to the official documentation:
-
pg144-axi-gpio.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf
-
pg142-axi-uartlite.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_uartlite/v2_0/pg142-axi-uartlite.pdf
5.6 SWD Interface Connection
In the official DesignStart IP core materials, in addition to the Cortex-M3 processor, there is also a DAP-Link debugging core. If you want to use the DAP-Link debugger, you need to add this IP core.
Here we will not use the DAP-Link debugger, but instead use Jlink in SWD mode. The SWD mode requires two lines, one for the SWCLK clock signal and one for the SWDIO bidirectional data signal. The processor provides three pins: SWDI, SWDO, and SWDOEN. We also need to implement a bidirectional port module.
The bidirectional port module implemented based on the IOBUF primitive is as follows:
module swdio_tri_buffer(
//Inputs
input swd_o,
input swd_oe,
//Outputs
output swd_i,
//Inouts
inout swd_io
);
IOBUF swd_iobuf_inst(
.O(swd_i),
.I(swd_o),
.IO(swd_io),
.T(!swd_oe)
);
endmodule
Add it to our design.
The final Block Design is shown in the following figure:
5.7 Allocate Peripheral Base Addresses
After adding the peripheral IPs, we also need to allocate base addresses and spaces for the peripherals. In the address editing box, right-click and select auto-allocate.
After allocation, use the design validation (Validate Design) function to check the legality of the current Block Design connections.
5.8 Generate Wrapper and Instantiate to Top Level
To facilitate the subsequent addition of custom FPGA logic modules, we will instantiate the Cortex-M3 soft core processor as a processor in the top-level design.
Right-click on the Block Design source file, first select Generate Output Products
, wait patiently for the generation to complete, and then select Create HDL Wrapper
.
After that, a wrapper verilog file will be generated.
Create a top-level file top_hdl.v and save it in the rtl folder, and instantiate the wrapper in the top level.
module top_hdl(
//Inputs
input clk,
input rst_n,
input swclk,
input uart_rxd,
input [3:0] sw,
//Outputs
output [3:0] led,
output uart_txd,
//Inouts
inout swdio
);
cm3_core_wrapper cm3_core_wrapper_ut0(
//Inputs
.cm3_clk(clk),
.cm3_resetn(rst_n),
.cm3_gpio_in_tri_i(sw[3:0]),
.cm3_swclk(swclk),
.cm3_uart_rxd(uart_rxd),
//Outputs
.cm3_gpio_out_tri_o(led[3:0]),
.cm3_uart_txd(uart_txd),
//Inouts
.cm3_swdio(swdio)
);
endmodule //top_hdl end
5.9 Pin Assignment
After synthesis is complete, use Vivado’s graphical tool for pin assignment, especially noting to connect SWDIO and SWDCLK to the pin headers for easy use of the external Jlink debugger to download ARM programs.
Alternatively, you can directly create an XDC file and use constraint statements for pin assignment.
Some constraint statements:
set_property PACKAGE_PIN R4 [get_ports clk]
set_property PACKAGE_PIN V13 [get_ports swclk]
set_property PACKAGE_PIN V14 [get_ports swdio]
set_property PACKAGE_PIN E14 [get_ports uart_rxd]
set_property PACKAGE_PIN D17 [get_ports uart_txd]
set_property PACKAGE_PIN U7 [get_ports rst_n]
set_property PACKAGE_PIN V9 [get_ports {led[3]}]
set_property PACKAGE_PIN Y8 [get_ports {led[2]}]
set_property PACKAGE_PIN Y7 [get_ports {led[1]}]
set_property PACKAGE_PIN W7 [get_ports {led[0]}]
set_property PACKAGE_PIN T4 [get_ports {key[3]}]
set_property PACKAGE_PIN T3 [get_ports {key[2]}]
set_property PACKAGE_PIN R6 [get_ports {key[1]}]
set_property PACKAGE_PIN T6 [get_ports {key[0]}]
If your board is the same as mine (Zedboard Pro), you can directly use the above pin constraints.
If the clock pin you assigned is not the global clock pin of the FPGA, you need to add a BUFG primitive for buffering.
5.10 Bitstream File Generation and Download
My board uses QSPI Flash. To improve download and startup speed, when generating the bitstream, configure the generation options: Data Compression, 50M Read Speed, 4 Data Lines.
Or directly use XDC statements for constraints:
set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
The above constraints are not mandatory, but are intended to improve download and configuration speed.
Wait patiently for the project synthesis to complete and the bitstream file to be generated. The synthesis speed is related to the processor main frequency and core count.
Similar to conventional FPGA download methods, download the generated soft core bit file to the FPGA internal using the Xilinx downloader, and do not burn it to the external SPI Flash yet.
If you do not have a Xilinx downloader, you can refer to previous articles to make a JTAG-HS2 downloader!
Open source, low-cost Xilinx FPGA downloader
5.11 Jlink Connection Test
After the download is complete, what is running inside the FPGA is now an ARM Cortex-M3 based soft core processor. You can connect to the chip using debugging tools such as Jlink.
Connect the SWCLK and SWDIO of the Jlink debugger to the assigned pins V13 and V14.
If you do not have a Jlink, you can also refer to previous articles to make a Jlink-OB!
Step-by-Step Guide to Making a Jlink-OB Debugger
To develop the DesignStart Cortex-M3 soft core program using Keil, you need to first install a dedicated device package for DesignStart.
The download address is as follows:
https://keilpack.azureedge.net/pack/Keil.V2M-MPS2_DSx_BSP.1.1.0.pack
Open an STM32 Keil project, change the device to the newly installed ARM DS_CM3, and in the Option->Debug-Setting
interface, select SWD mode. The first connection will prompt you to select a device. Here, choose Cortex-M3:
If the above configurations are correct, you will see the connected ARM Cortex-M3 core. If not, it indicates that there is an error in the FPGA project configuration, and you need to confirm whether it is consistent with the above configuration process.
At this point, the ARM Cortex-M3 soft core has been basically set up. Next, we will use Keil to write the ARM core program to control GPIO and UART.
6.Cortex-M3 Soft Core Program Design
Similar to the conventional ARM Cortex-M3 core microcontroller development process, use Keil to create a new project, source files, and implement GPIO control and UART data writing by reading and writing specified registers based on the peripheral usage manual, compile, download, and debug.
In the previously created cortex_m3_on_xc7a100t
folder, create a folder named mdk_prj to save the Keil-MDK project, and create the following three folders:
application //User source files
object //Compiled files
project //Keil project files
6.1 Create Keil Project
Open Keil-MDK, select Project->New Project
, create a new project named ds_cm3_prj, and save it in the project directory.
Select the device model as our newly installed ARM Cortex-M3 DS_CM3 core.
In the component management interface, add CMSIS core files and Startup initialization files:
And organize the files as follows:
6.2 Set RAM and ROM Addresses
In the project options, set the starting address of the on-chip ITCM to 0x0 and size to 64K, and the on-chip DTCM starting address to 0x20000000 and size to 64K:
The starting address comes from the system memory address mapping in the user manual, which shows the starting addresses of ITCM and DTCM:
The size is set according to the size we configured in the Cortex-M3 core:
After setting, create a main.c file and enter the following content, compile the project, and there should be no error output.
#include "DS_CM3.h"
#include "system_DS_CM3.h"
int main(void)
{
while(1)
{
}
}
6.3 GPIO Input and Output Control
By checking the AXI GPIO usage manual, the data register offset address of channel 1 is 0, and the data register offset address of channel 2 is 0x08. According to the connections in Vivado, the LED is connected to channel 1, and the button is connected to channel 2, so we only need to read and write these two register addresses to control the LED and read the status of the switch.
In the Vivado address allocation interface, you can see that the base addresses of GPIO and UART are 0x4000_0000 and 0x4060_0000 respectively.
LED control and switch reading:
*(volatile uint32_t *) (0x40000000+0x0) = 0x0f; //Write 1 to the low 4 bits of GPIO channel 1
*(volatile uint32_t *) (0x40000000+0x0) = 0x00; //Write 0 to the low 4 bits of GPIO channel 1
uint32_t sw = 0;
sw = *(uint32_t *) (0x40000000+0x08); //Get the 32-bit input status of GPIO channel 2
6.4 Serial Data Sending and Receiving
To send a byte of data to the serial port, write it into the FIFO:
while((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x08 != 0x08); //Wait for the transmit FIFO to be not full
*(volatile uint32_t *) (0x40600000+0x04) = 0x41; //Write character 'A'=0x41 to the transmit FIFO
To receive a byte of data from the serial port:
uint8_t dat = 0;
if((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x01 == 1) //If there is data in the receive FIFO
dat = (*(volatile uint32_t *)(0x40600000 + 0x00)); //Read 1 byte of data from the receive FIFO.
For detailed descriptions of the AXI GPIO and AXI UART registers, please refer to the official documentation:
-
pg144-axi-gpio.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_gpio/v2_0/pg144-axi-gpio.pdf
-
pg142-axi-uartlite.pdf
https://www.xilinx.com/support/documentation/ip_documentation/axi_uartlite/v2_0/pg142-axi-uartlite.pdf
6.5 Delay Function Implementation
To make the LED changes visible to the human eye, we need to use a delay function to delay the on and off.
Use the system tick timer to implement a delay function:
volatile uint32_t cnt = 0; //volatile type
void SysTick_Handler(void)
{
cnt++;
}
void delay_ms(uint32_t t)
{
cnt = 0;
while(cnt-t>0);
}
To make the delay function accurate, we also need to change the system clock frequency in the project to match the clock configured in the FPGA core.
The completed main.c file content:
#include "DS_CM3.h"
#include "system_DS_CM3.h"
//C library
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#define BASEADDR_LED 0x40000000
#define BASEADDR_UART 0x40600000
#define CHANNEL_LED 1
#define CHANNEL_SW 2
#define XGPIO_CHAN_OFFSET 8
#define XGpio_WriteReg(BaseAddress, RegOffset, Data) Xil_Out32((BaseAddress) + (RegOffset), (uint32_t)(Data))
#define XGpio_ReadReg(BaseAddress, RegOffset) XGpio_In32((BaseAddress) + (RegOffset))
#define XUL_TX_FIFO_OFFSET 4 /* transmit FIFO, write only */
#define XUL_STATUS_REG_OFFSET 8 /* status register, read only */
#define XUL_SR_TX_FIFO_FULL 0x08 /* transmit FIFO full */
#define XUartLite_GetStatusReg(BaseAddress) XUartLite_ReadReg((BaseAddress), XUL_STATUS_REG_OFFSET)
#define XUartLite_ReadReg(BaseAddress, RegOffset) XGpio_In32((BaseAddress) + (RegOffset))
#define XUartLite_IsTransmitFull(BaseAddress) \
((XUartLite_GetStatusReg((BaseAddress)) & XUL_SR_TX_FIFO_FULL) == \
XUL_SR_TX_FIFO_FULL)
#define XUartLite_WriteReg(BaseAddress, RegOffset, Data) Xil_Out32((BaseAddress) + (RegOffset), (uint32_t)(Data))
volatile uint32_t cnt = 0;
void SysTick_Handler(void)
{
cnt++;
}
void delay_ms(uint32_t t)
{
cnt = 0;
while(cnt-t>0);
}
uint32_t XGpio_In32(uint32_t Addr)
{
return *(volatile uint32_t *) Addr;
}
void Xil_Out32(uint32_t Addr, uint32_t Value)
{
volatile uint32_t *LocalAddr = (volatile uint32_t *)Addr;
*LocalAddr = Value;
}
uint32_t XGpio_DiscreteRead(uint32_t Addr, uint8_t Channel)
{
return XGpio_ReadReg(Addr, (Channel-1)*XGPIO_CHAN_OFFSET);
}
void XGpio_DiscreteWrite(uint32_t Addr, uint8_t Channel, uint32_t Data)
{
XGpio_WriteReg(Addr, (Channel-1)*XGPIO_CHAN_OFFSET, Data);
}
void XUartLite_SendByte(uint32_t BaseAddress, uint8_t Data)
{
while (XUartLite_IsTransmitFull(BaseAddress));
XUartLite_WriteReg(BaseAddress, XUL_TX_FIFO_OFFSET, Data);
}
void cm3_print(const char *ptr)
{
while (*ptr != (char)0) {
XUartLite_SendByte(BASEADDR_UART, *ptr);
ptr++;
}
}
void MyUartPrintf(char *fmt,...)
{
unsigned char UsartPrintfBuf[296];
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), (const char *)fmt, ap);
va_end(ap);
while(*pStr != 0)
{
XUartLite_SendByte(BASEADDR_UART, *pStr);
pStr++;
}
}
void led_blink(void)
{
XGpio_DiscreteWrite(BASEADDR_LED, CHANNEL_LED, 0);
delay_ms(500);
XGpio_DiscreteWrite(BASEADDR_LED, CHANNEL_LED, 0xf);
delay_ms(500);
}
int main(void)
{
uint32_t sw = 0;
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock/1000);
cm3_print("Hello DesignStart ARM Cortex-M3 on FPGA Xilnx Artix-7 XC7A100T \r\n");
MyUartPrintf("SystemCoreClock = %ld\r\n", SystemCoreClock);
while(1)
{
led_blink();
sw = XGpio_DiscreteRead(BASEADDR_LED, CHANNEL_SW);
MyUartPrintf("key state = %d-%d-%d-%d\r\n", sw>>3, sw>>2&1, sw>>1&1, sw&1);
}
}
The implemented function is that 4 LEDs blink every 100ms, while the serial port outputs the real-time status of the switch.
After compiling without errors, the program can be downloaded.
6.6 Flash Programming Algorithm Generation
Using Jlink to download the program requires specifying a Flash programming algorithm, but the algorithms that come with Keil do not include what we need:
Therefore, we need to customize a Flash programming algorithm. Open the
folder in the Keil installation directory, copy the
ARMlash_Template
folder, and rename it to DS_CM3:
Open the Keil project inside:
This project allows you to set the Flash starting address, size, erase size, etc.
Fill in the following content in the FlashDev.c file, keeping it consistent with our previous ITCM configuration, starting address 0x0, size 64K:
#include "..\FlashOS.H" // FlashOS Structures
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, // Driver Version, do not modify!
"MyCM3onFPGA", // Device Name
ONCHIP, // Device Type
0x00000000, // Device Start Address
0x00010000, // Modify to 64KB
1024, // Programming Page Size
0, // Reserved, must be 0
0xFF, // Initial Content of Erased Memory
100, // Program Page Timeout 100 mSec
3000, // Erase Sector Timeout 3000 mSec
// Specify Size and Address of Sectors
0x010000, 0x000000, // Only one sector, starting address is 0
SECTOR_END
};
The FlashPrg.c file implements some functions for erasing the storage area:
#include "..\FlashOS.H" // FlashOS Structures
#include "string.h"
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
return (0); // Finished without Errors
}
int UnInit (unsigned long fnc) {
return (0); // Finished without Errors
}
int EraseChip (void) {
memset((unsigned char *)0, 0, 0x10000);
return (0); // Finished without Errors
}
int EraseSector (unsigned long adr) {
memset((unsigned char *)adr, 0, 1024);
return (0); // Finished without Errors
}
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
memcpy((unsigned char *)adr, buf, sz);
return (0); // Finished without Errors
}
After compiling without errors, a FLM file will be generated in the project directory.
Copy it to the parent directory:
6.7 Compile, Download, and Run
Then open our ARM core Keil project and add the DS_CM3 Flash programming algorithm:
Click the download button to download the ARM program to the ARM core:
You can see that the LED blinks every 500ms, and the serial port outputs data every 1s. At the same time, pressing the button outputs the status of the button through the serial port.
Like other ARM core chips, it also supports online debugging:
Since the ARM program is downloaded to the RAM storage area of the Cortex-M3 soft core, the program will be lost after power off. How to download the program to the external SPI Flash, I have not successfully implemented.
7.Open Source Address
The PDF file of this article, Vivado project, Keil project, Keil device support package, Flash programming algorithm files, reference documents for peripheral IP, ARM M3 soft core IP materials, etc. have been open-sourced to Github and Gitee, with the following addresses:
-
Gitee
git clone https://gitee.com/whik/cortex_m3_on_xc7a100t.git
-
Github
git clone https://github.com/whik/cortex_m3_on_xc7a100t.git
Alternatively, follow me (Public Account: Electronic Circuit Development Learning) and reply in the background: 220327,
to directly obtain the download link for the above material package, which can be copied to the browser for direct download.
Reference Materials
This article references two video tutorials published by Bilibili chip design expert Hongxiang (ID: 4253239):
-
Using Vivado Block Design to Design ARM DesignStart M3 Based Soft Core SoC (ID: BV1bp4y187wf)
-
Using Keil to Design Software Programs Based on ARM DesignStart M3 Soft Core (ID: BV1cy4y147Sc)
If the method described in this article cannot be successfully implemented, you can refer to the above two video tutorials.
Friends interested in studying soft core processors implemented on FPGA can join the group for mutual exchange and learning: Hongxiang Chip Design Enthusiasts, QQ Group 541294921
In fact, in previous years, the ARM Cup competition topics in 2019/2020/2021 were all based on the ARM Cortex-M3 DesignStart program to implement SoC design, and many excellent design works were submitted.
However, there has not been a detailed and systematic construction process found on the internet. This article is merely a personal summary note, hoping to help those who want to learn the knowledge of building ARM soft cores on FPGA.
Leave a Comment
Your email address will not be published. Required fields are marked *