1.必要的基础知识

为了更快的完成在FPGA上实现ARM Cortex-M3软核,一些必要的基础知识还是要有的!

  • FPGA开发基础知识,如FPGA开发流程,设计、综合、布局、布线、约束、下载

  • Xilinx Vivado开发环境使用基础,如BlockDesign设计方式,管脚分配,Bit流文件生成与下载

  • ARM Cortex-M3内核的使用基础,如STM32、MM32、GD32、CH32等微控制器的开发。

  • Keil-MDK开发环境的使用基础,基本的工程建立、编译、下载流程。

如果以上知识都具备,那么,恭喜你!可以在2小时内完成ARM Cortex-M3软核在FPGA上的实现。

2.Cortex-M3 FPGA IP核下载

首先,我们需要从ARM官网上获取ARM Cortex-M3 FPGA软核IP包

下载地址如下:

https://silver.arm.com/browse/AT426

文件名称:Cortex-M3 DesignStart FPGA-Xilinx edition(r0p1-00rel0)

文件大小:7.52MB

MD5SUM:cd67536c29023429cde47130d51b6f49

官网下载需要先注册账号,如果下载速度很慢,可以在公众号后台回复:220318,获取下载链接,复制到浏览器下载。

ARM官网

压缩包解压之后,共有4个文件夹:

压缩包内容

各个文件夹存放的内容:

  • docs

存放ARM Cortex-M3处理器参考手册、DesignStart FPGA版本使用说明、基于Arty-A7开发板的顶层BlockDesign框图等文件。

  • hardware

存放基于Digilent Arty-A7开发板的Vivado工程,顶层BlockDesign文件,管脚约束文件,Testbench文件等。

  • software

存放Keil-MDK工程,SPI Flash的编程算法文件等。

  • vivado

包括DesignStart Cortex-M3 Xilinx FPGA版本的IP核文件,其中Arm_ipi_repository文件夹就是内核源文件了,IP文件内容已经加密,没有可读性。

IP核源码

3.硬件准备

为了完成DS CM3在FPGA上的搭建,我们至少需要以下硬件:

  • 一块Artix-7™开发板,用于构建Cortex-M3软核SoC,我使用的是正点原子达芬奇Pro开发板,FPGA型号为XC7A100T

  • Xilinx FPGA下载器,用于下载软核Bit流到FPGA,如Platform Usb Cable,JTAG-HS2/HS3等。

  • ARM Cortex-M‍3调试器,用于调试ARM核程序下载和调试,如JlinkV9,Jlink-OB等。

官方的DS CM3 IP核是基于Digilent的Arty-A7开发板,FPGA型号为XC7A35T/100T,Vivado版本为v2019.1,如果你手头正好有这块开发板,那么可以直接使用官方提供的示例工程。

Digilent Arty-A7开发板:

arty-a7开发板

正点原子达芬奇Pro开发板:

正点原子达芬奇Pro开发板

4.软件准备

  • Xilinx Vivado开发环境,官方建议版本为2018.2以上,我使用的是2018.3版本

  • Keil MDK开发环境,如5.33版本

  • DS_CM3的Keil器件包

    从Keil官网上下载DesignStart Cortex-M3所专用的器件支持包,下载链接如下:

    https://keilpack.azureedge.net/pack/Keil.V2M-MPS2_DSx_BSP.1.1.0.pack

5.Cortex-M3软核搭建

准备好以上软硬件,就可以开始Cortex-M3软核的搭建了。

首先,新建一个文件夹,命名为cortex_m3_on_xc7a100t,用于存放本次示例所有的工程文件,并新建以下几个文件夹:

目录结构

每个文件夹的功能:

  • bd文件夹

    用来存放BlockDesign设计

  • cm3_core文件夹

    用来存放的是ARM Cortex-M3内核IP核文件,

  • doc文件夹

    用来存放设计文档

  • flash文件夹

    用来存放生成的bit和mcs文件

  • rtl文件夹

    用来存放用户设计的verilog源文件

  • xdc文件夹

    用来存放管脚、时序约束文件

其中cm3_core文件夹,需要将官方压缩文件文件中的Arm_ipi_repository文件夹复制过来,路径为AT426-BU-98000-r0p1-00rel0\vivado\Arm_ipi_repository

以上文件夹准备好之后,就可以开始新建工程了。

5.1 新建Vivado工程

打开Vivado 2018.3,打开工程创建向导,输入工程名称,工程的存放路径为之前我们新建的文件夹。

新建工程

选择FPGA芯片的完整型号:XC7A100TFGG484

选择芯片型号

最终创建完成之后的工程目录

Vivado工程目录

5.2 添加IP核搜索路径

为了能在BlockDesign中搜索到ARM Cortex-M3处理器IP核,我们需要把ARM 软核IP所在的路径添加到搜索路径。

添加到搜索路径

5.3 创建BlockDesign设计

为了方便后续使用图形化的方式连接各IP核,我们采用BlockDesign图形化的设计方式,这样可以快速的搭建出一颗定制化的软核处理器。

新建BlockDesign,命名为cm3_core,保存到最初创建的bd文件夹中。

在画布中添加Cortex-M3处理器核:

添加ARM核

双击Cortex-M3 IP核进行一些基本配置,我们不需要Trace功能,选择No Trace,使用SWD接口调试,禁用JTAG端口:

配置ARM核

指令空间和数据空间大小,这里设置成64KB,都不进行初始化。

ITCM核DTCM配置

5.4 添加一些必要的IP核

  • 时钟PLL

    用于提供给内核、总线、外设时钟,这里我们配置成50MHz单端输入,PLL输出配置成50MHz,如果时钟频率设置更高,综合后会提示WNS,TNS时序不满足,可能会影响系统的正常运行。

  • 处理器复位IP

    用于提供内核、外设、互联组件所需要的复位信号,不需要进行定制,保持默认设置。

  • 总线互联IP

    Cortex-M3内核为AHB总线,而且内部已经转换成了AXI3总线,而Xilinx官方提供的GPIO/UART等外设IP核是AXI4-Lite总线,所以需要添加一个总线互联矩阵,用于将不同协议进行转换,从机数量配置为1,主机数量配置为2,连接到处理器的SYS总线。

  • 基本逻辑门IP

    Cortex-M3内核需要低电平复位,而复位IP输出为高电平复位,需要在中间插入一个非门来进行转换。

  • 常量IP

    本次软核搭建不涉及中断部分,所以IRQ和NMI都给定常量0即可,如果需要将中断接入处理器,可以通过Concat核将多个中断源合并成一个连接到IRQ。

将以上IP添加到BlockDesign画布中,并按照下图进行连接:

原理图连接

从官方手册中可以知道,ARM提供的软核IP中已经包括了ITCM和DTCM存储器,所以我们无需添加外部的BRAM来作为程序和数据的存储区。

Cortex-M3内核结构

内核中提供ITCM和DTCM都是基于RAM实现,这也就意味着后续我们使用Keil下载程序只是下载到RAM中,掉电数据会丢失。

至此,ARM Cortex-M3处理器内核就搭建完成了,下面来添加GPIO和UART外设。

5.5 添加GPIO和UART外设

一些常用的单片机,如STM32,芯片内部的TIM、UART、SPI、CAN等外设一般是固定数量的,而我们使用FPGA来搭建ARM软核SoC就比较灵活了,如果你不需要SPI,那就不用添加SPI外设,需要10个UART就添加10个UART,外设配置比较灵活,当然这些外设都是基于FPGA逻辑资源实现的,实际添加的数量会受限于FPGA芯片的逻辑资源大小。

下面以添加一组AXI GPIO和一组AXI UART为例,介绍如何使用ARM软核来控制这两个外设。

Xilinx官方提供的AXI GPIO外设具有以下特性:

  • 内部有两个通道,通道1和通道2,每个通道最多支持32个管脚

  • 每个管脚可以配置成输入或输出模式

  • 每个管脚可以设置复位初值

  • 支持中断输出

提供的AXI UART外设有以下特性:

  • 全双工

  • 支持5-8位数据位

  • 支持奇偶校验

  • 可配置波特率110-230400

这里我们将GPIO配置成双通道,通道1为输出模式,低4位用于连接LED,通道2为输入模式,低4位用于连接按键。

GPIO配置

UART配置成115200波特率,8位数据位,无奇偶校验。

UART配置

配置完成之后,将它们连接的到互联IP的主机接口上:

原理图连接

这两组IP的时钟可以和处理器使用同样的时钟,复位可以使用复位IP输出的外设复位信号。

关于AXI GPIO和AXI UART的详细使用,可以查看官方文档:

  • 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接口的引出

官方的DesignStart IP核资料中,除了Cortex-M3处理器,还有一个DAP-Link调试核,如果使用DAP-Link调试器需要添加这个IP核。

DAP-Link

这里我们不使用DAP-Link调试器,而是使用Jlink SWD模式。SWD模式一共需要两根线,一个是SWCLK时钟信号,一个是SWDIO双向数据信号,处理器提供了3个管脚:SWDI,SWDO和SWDOEN,我们还需要实现一个双向端口模块。

基于IOBUF原语实现的双向端口模块,内容如下:

module swdio_tri_buffer(//Inputsinput swd_o,input swd_oe,//Outputsoutput swd_i,//Inoutsinout swd_io
);IOBUF swd_iobuf_inst(.O(swd_i),.I(swd_o),.IO(swd_io),.T(!swd_oe)
);endmodule

将它添加到我们的设计中。

SWD接口连接

最终的BlockDesign设计如下图所示:

原理图连接

5.7 分配外设基地址

添加完外设IP之后,我们还需要对外设进行基地址和空间分配,在地址编辑框,右键选择自动分配。

基地址分配

分配完成之后,使用设计验证(Validate Design)功能,可以检查当前BlockDesign设计连接的合法性。

验证设计

5.8 生成Wrapper并例化到顶层

为了方便后续添加自定义的FPGA逻辑模块,我们将Cortex-M3软核处理器作为一个处理器例化到顶层设计中。

在BlockDesign源文件上右键,先选择Generate Output Products,耐心等待生成完成之后,选择Create HDL Wrapper

生成Wrapper

之后就会生成一个_wrapper的verilog文件。

新建顶层文件top_hdl.v并保存到rtl文件夹,将_wrapper例化到顶层。

module top_hdl(//Inputsinput clk,input rst_n,input swclk,input uart_rxd,input [3:0] sw,//Outputsoutput [3:0] led,output uart_txd,//Inoutsinout 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 管脚分配

综合(Synthesis)完成之后,使用Vivado的图形化工具进行管脚分配,尤其注意要将SWDIO和SWDCLK引出到排针管脚上,方便后续使用外接的Jlink调试器进行ARM程序下载。

分配管脚

或者直接新建XDC文件,使用约束语句进行管脚分配。

部分约束语句:

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]}]

如果你的板子和我的(正点原子达芬奇Pro)一样,那么可以直接使用以上管脚约束。

如果你分配的时钟管脚不是FPGA的全局时钟管脚,需要添加BUFG原语进行缓冲。

5.10 Bit流文件生成和下载

我的板子使用的是QSPI Flash,为了提高下载和启动速度,在生成Bit流时,配置生成选项:数据压缩、50M读取速度,4位数据线

生成Bit流配置

或者直接使用XDC语句进行约束:

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]

以上约束不是必须的,只是为了提高下载和配置速度。

耐心等待工程综合完成,生成Bit流文件,综合的速度和处理器主频、核心数有关。

和常规的FPGA下载方式一样,将生成的软核Bit文件通过Xilinx下载器下载到FPGA内部,先不要固化到外部SPI Flash 。

手头没有Xilinx下载器的,可以参考之前的文章,自己做一个JTAG-HS2下载器

开源、低成本的Xilinx FPGA下载器

5.11 Jlink连接测试

下载完成之后,现在FPGA内部运行的就是一颗基于ARM Cortex-M3的软核处理器了,使用Jlink等调试工具可以连接到芯片。

将Jlink调试器的SWCLK和SWDIO连接到我们分配的管脚V13和V14上。

手头没有Jlink的,也可以参考之前的文章,自己做一个Jlink-OB

手把手教你制作Jlink-OB调试器

使用Keil开发DesignStart Cortex-M3软核的程序,需要先安装一个DesignStart专用的器件包。

下载地址如下:

https://keilpack.azureedge.net/pack/Keil.V2M-MPS2_DSx_BSP.1.1.0.pack

打开一个STM32 Keil工程,器件修改为刚刚安装的ARM DS_CM3,在Option->Debug-Setting界面中选择SWD方式,第一次连接会提示需要选择一个器件,这里选择Cortex-M3:

选择器件型号

如果以上配置均正确,就能看到已经连接到的ARM Cortex-M3核心。如果没有,说明FPGA工程配置有错误,需要确认是否和以上配置流程一致。

连接到ARM核心

至此,ARM Cortex-M3软核基本搭建完成,接下来我们使用Keil来编写ARM核的程序,实现GPIO和UART的控制。

6.Cortex-M3软核程序设计

和常规的ARM Cortex-M3内核单片机开发流程类似,使用Keil新建工程,源文件,根据外设使用手册,读写指定的寄存器实现GPIO的控制,UART数据写入,编译下载,调试。

在之前创建的cortex_m3_on_xc7a100t文件夹下,新建mdk_prj文件夹,用于保存Keil-MDK的工程,并新建以下3个文件夹:

application        //用户源文件
object            //编译生成的文件
project            //Keil的工程文件

6.1 新建Keil工程

打开Keil-MDK,选择Project->New Project,新建一个工程,命名为ds_cm3_prj,保存到project目录下。

Keil工程目录

器件型号选择我们新安装的ARM Cortex-M3 DS_CM3内核。

选择器件型号

组件管理界面中,添加CMSIS内核文件和Startup启动文件:

添加内核文件

并按照如下结构组织文件:

文件结构

6.2 设置RAM和ROM地址

在工程选项中设置片上ITCM的起始地址0x0、大小64K,片上DTCM起始地址0x20000000、大小64K:

RAM地址配置

起始地址来源于使用手册中图4-1系统内存地址映射,可以看到其中ITCM和DTCM的起始地址:

ITCM和DTCM起始地址

大小是我们在Cortex-M3内核配置中设置的大小:

ITCM和DTCM大小

设置完成之后,新建main.c文件,输入以下内容,编译工程,应该无错误输出。

#include "DS_CM3.h"
#include "system_DS_CM3.h"int main(void)
{while(1){}
}

6.3 GPIO输入输出控制

通过查看AXI GPIO的使用手册,通道1的数据寄存器偏移地址为0,通道2的数据寄存器偏移地址为0x08,根据Vivado中的连接,LED连接到通道1,按键连接到通道2上,所以只需要对这两个寄存器地址进行读写,就可以实现LED的控制和拨码开关状态的读取。

AXI GPIO寄存器定义

在Vivado地址分配界面,可以看到GPIO和UART的基地址分别为:0x4000_0000和0x4060_0000。

外设基地址

LEL控制和拨码开关读取:

*(volatile uint32_t *) (0x40000000+0x0) = 0x0f;    //GPIO通道1低4位写1
*(volatile uint32_t *) (0x40000000+0x0) = 0x00;    //GPIO通道1低4位写0uint32_t sw = 0;
sw = *(uint32_t *) (0x40000000+0x08);    //获取GPIO通道2的32位输入状态

6.4 串口数据发送和接收

向串口发送FIFO写入一字节数据:

while((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x08 != 0x08);    //等待发送FIFO不满
*(volatile uint32_t *) (0x40600000+0x04) = 0x41;    //向串口发送FIFO写入字符'A'=0x41

从串口接收一字节数据:

uint8_t dat = 0;
if((*(volatile uint32_t *)(0x40600000 + 0x08)) & 0x01 == 1)    //串口接收FIFO中有数据dat = (*(volatile uint32_t *)(0x40600000 + 0x00));        //从接收FIFO中读取1字节数据。

关于AXI GPIO和AXI UART寄存器的详细说明,可以查看官方文档:

  • 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 延时函数实现

为了让LED的变化,可以被人眼所看到,需要使用延时函数对亮灭进行延时。

使用系统滴答定时器实现一个延时函数:

volatile uint32_t cnt = 0;    //volatile类型void SysTick_Handler(void)
{cnt++;
}void delay_ms(uint32_t t)
{cnt = 0;while(cnt-t>0);
}

为了让延时函数准确延时,我们还需要更改工程中的系统时钟频率,和FPGA中配置的内核时钟保持一致。

系统时钟

完成的main.c文件内容:

#include "DS_CM3.h"
#include "system_DS_CM3.h"
//C库
#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);}
}

实现的功能是,4颗LED每100ms闪烁一次,同时串口输出此时拨码开关的实时状态。

编译无误后,就可以进行程序下载了。

6.6 Flash编程算法生成

使用Jlink下载程序需要指定Flash编程算法,但是Keil自带的算法中并没有我们所需要的:

下载算法

所以我们需要定制一份Flash编程算法,打开Keil安装目录下的\ARM\Flash文件夹,将_Template文件夹复制出一份,并命名为DS_CM3,

复制模板

打开其中的Keil工程:

下载算法

这个工程可以自己设置要编程的Flash起始地址、大小,擦除大小等。

FlashDev.c文件填入以下内容,和我们之前ITCM的配置保持一致,起始地址0x0,大小64K:

#include "..\FlashOS.H"        // FlashOS Structuresstruct FlashDevice const FlashDevice  =  {FLASH_DRV_VERS,             // Driver Version, do not modify!"MyCM3onFPGA",              // Device Name ONCHIP,                     // Device Type0x00000000,                 // Device Start Address0x00010000,                 // 修改为64KB1024,                       // Programming Page Size0,                          // Reserved, must be 00xFF,                       // Initial Content of Erased Memory100,                        // Program Page Timeout 100 mSec3000,                       // Erase Sector Timeout 3000 mSec// Specify Size and Address of Sectors0x010000, 0x000000,         // 只有一个扇区,起始地址为0SECTOR_END
};

FlashPrg.c文件,实现一些存储区擦除的函数:

#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
}
编译无误后,会在工程目录下生成一个FLM文件。

新生成的下载算法

将它复制到上一级目录:

新生成的下载算法

6.7 编译下载运行

再打开我们的ARM核Keil工程,添加DS_CM3 Flash编程算法:

添加Flash编程算法

点击下载按钮,把ARM程序下载到ARM核:


可以看到LED每500ms闪烁一次,串口数据每1s输出一次,同时按下按键,串口输出按键的状态。


和其他ARM内核芯片一样,也是支持在线调试的:

43

由于ARM程序是下载到Cortex-M3软核内的RAM存储区,所以掉电后程序会丢失。如何将程序下载到片外的SPI Flash中,我还没有成功实现。

-END-


往期推荐:点击图片即可跳转阅读

嵌入式与单片机之间的关系是什么?

考研还是工作?看完这个给你答案!

拆解飞机黑匣子,看看内部构造、PCB及芯片

19年三本毕业,做了三年的嵌入式软件。

我是张巧龙,一名教电子的大学老师,欢迎关注!

用FPGA搭建一个STM32内核?相关推荐

  1. FPGA的设计艺术(17)如何搭建一个简易的逻辑测试平台?

    前言 提到FPGA逻辑的仿真,一般指的是行为仿真或者功能仿真,还有人会称为前仿,不包含时间延迟信息,只验证逻辑功能.对于小模块的仿真,需要写一个测试文件,英文是testbench,即测试平台.在tes ...

  2. 如何搭建一个数据库服务器平台 .

    玩Oracle 2年多了,从接触Oracle 到现在,一直没有停止过学习. 要学的东西太多,刚入门的时候是这样的感觉,现在还是这样的感觉. 有时候也在想,还要学多长时间才能感觉自我良好了,有十足的自信 ...

  3. 记:一次意外JTAG使用引发对于STM32内核的了解

    文章目录 0x01 数据异常? 0x02 排查过程 0x03 究其原因 下面有空更新,内部解锁FLASH操作.出差期间发文,很累了,暂时先写到这里 更多 0x01 数据异常? 在一次偶然的情况下,大佬 ...

  4. 【自动驾驶】如何利用深度学习搭建一个最简单的无人驾驶系统

                                                 新智驾按:本文为新智驾独家专栏,作者系佐思产研研究总监周彦武,新智驾经授权发布. 国内最牛的无人驾驶厂家的运算 ...

  5. 苹果cms模板_苹果CMS V10 开源影视系统,搭建一个属于自己的影视网

    苹果CMS程序是一套采用PHP+MYSQL环境下运行的完善而强大的快速建站系统. 经过近多年的开发经验和技术积累,苹果CMS程序已逐步走向成熟,在易用性和功能上已经成为同行中的佼佼者. 程序体积小-& ...

  6. 树莓派如何与物联网平台交互(搭建一个树莓派网关)(一)

    树莓派如何与物联网平台交互(搭建一个树莓派网关)(一) 一.功能描述 ​树莓派网关采集485温湿度传感器以及485门磁开关状态数据上报到涂鸦云平台:同时收到云端的指令,树莓派网关处理之后,控制继电器动 ...

  7. 利用OpenStreetMap(OSM)数据搭建一个地图服务

    图 利用OSM数据简单发布的北京地图服务   一.OSM是什么 开放街道图(OpenStreetMap,简称OSM)是一个网上地图协作计划,目标是创造一个内容自由且能让所有人编辑的世界地图(wiki: ...

  8. keil5中新建一个STM32工程

    前言 相信很多stm32的初学者对于创建一个新的工程还是感觉比较困难的,即使学习了一段时间的stm32,创建一个新工程还是会感到无所适从.相信大家在学习stm32之初都是把整个例程工程文件copy下来 ...

  9. 一个stm32简单程序的编译

    MDK 是 Microcontroller Development Kit 的缩写,ARM 公司出品,是目前针对 ARM 处理器,尤其是 Cortex-M 内核处理器的最佳开发工具. 目录 一.MDK ...

最新文章

  1. java获取apk启动activity_[RK3399] android7.1 设置开机启动apk
  2. layer.load 支持文字内容
  3. Python3比较运算符
  4. 【数据结构与算法】之深入解析“寻找旋转排序数组中的最小值II”的求解思路与算法示例
  5. mac 环境变量设置
  6. 第五十五期:MongoDB数据库误删后的恢复
  7. python爬虫用什么软件写_python爬虫怎么写
  8. 182. 查找重复的电子邮箱
  9. 容器编排技术 -- Kubernetes kubectl create secret 命令详解
  10. 大数据平台设计哲学的重构
  11. TCP\IP协议实践:wireshark抓包分析之链路层与网络层
  12. 银行客户交易行为预测:如何降低内存的使用量
  13. 2016上海计算机考试PS玉佩题,玉佩效果,教案,ps,实例教程.doc
  14. Rpg maker mv角色扮演游戏制作大师简介
  15. iso硬盘安装 凤凰os_多系统下成功硬盘安装phoenix OS
  16. istio 防故障流量控制
  17. 自动驾驶 Apollo 源码分析系列,感知篇(三):红绿灯检测和识别
  18. 性能优化,进无止境---CPU篇
  19. 【矩阵论】线性空间与线性变换(5)
  20. Doug Lea是谁?谁知道

热门文章

  1. SAP UI5应用访问OData metadata的url和Destination
  2. SAP ABAP实用技巧介绍系列之已知某个signature查找定义的方法
  3. 另一种无法enable ABAP source code tool的原因
  4. fragment in UI5 Smart Template and directive in Angular
  5. QHD DDIC is implemented via HANA
  6. 真心酸,程序员工作了十年,衣服和电脑包破了都没钱买新的
  7. 关于SAP成都研究院的一些微信公众号文章
  8. Word2019上面的MathType7.4插件忽然消失了【终极解决办法记录】
  9. python如何删除代码_Python如何删除除字母和数字之外的所有字符?(代码示例)
  10. LR中如何添加事务,参数化,检查点,集合点,思考时间等