ARM Cortex M0移植到FPGA

综述

本文将介绍如何将DesignStart Eval的示例SoC移植到FPGA上,该SoC使用M0内核。在开始之前,简明阐述本文实现思路,移植可分为两部分:硬件与软件。

  • 硬件移植:Verilog代码的移植、综合、布局布线与调试;在DesignStart Eval的包文件中,...\systems\cortex_m0_mcu\verilog文件夹下实现的有一参考示例,复制所需文件即可。
  • 软件移植:基于CMSIS与参考代码制作自己的库函数,寄存器的配置均需要与RTL中配置的保持一致。

移植该项目时,参考了许多官方文档与第三方书籍,其中最有价值的列举如下:

  • DUI0926B_cortex_m0_designstart_eval_guide.pdf,该手册介绍了示例SoC的结构与细节,用于硬件搭建。
  • DDI0479C_cortex_m_system_design_kit_r1p0_trm.pdf,该手册介绍了CMSDK系统模块,在后期构建库函数时颇有用处。
  • AMBA总线规范中文版V2.0.pdf,介绍了AMBA规范下各个接口的协议。
  • ARM Cortex-M0全可编程SoC原理及实现.pdf,总体上描述了M0的技术细节。

SoC概括

该SoC来自官方示例,可在systems文件夹下找到,顶层文件为cmsdk_mcu.v,我们要做的是:

  • 复制所需文件,包括M0核心与CMSDK文件。
  • 例化ROM/RAM,在FPGA内部二者均使用SRAM实现,在顶层文件中将cmsdk_ahb_romcmsdk_ahb_ramMEM_TYPE均修改为2,即用于FPGA。
  • 开启SWD调试的电源,找到cmsdk_mcu_system.v,在其核心例化文件中修改即可。
  • 新建顶层文件,加入MMCM用于配置时钟。
  • 将IO口绑定引脚,综合、布局布线、下载调试,笔者使用的是CMSIS-DAP。

实现的SoC总体框架为:

Cortex M0 SoC

该SoC的AHB地址映射为:

内存地址 功能
0x00000000-0x003FFFFF ROM,大小为2^AW
0x20000000-0x207FFFFF RAM,大小为2^AW
0x40000000-0x4000FFFF APB外设挂载地址
0x40010000-0x40010FFF GPIO #0
0x40011000-0x40011FFF GPIO #1
0x4001F000-0x4001FFFF 系统控制寄存器
0xF0000000-0xF0000400 系统ROM表

该SoC的APB地址映射为:

内存地址 功能
0x40000000-0x40000FFF Timer0
0x40001000-0x40001FFF Timer1
0x40002000-0x40002FFF Dual Timer
0x40004000-0x40004FFF UART0
0x40005000-0x40005FFF UART1
0x40006000-0x40006FFF UART2
0x40008000-0x40008FFF Watchdog
0x4000B000-0x4000BFFF APB test slave

准备文件

示例SoC的顶层视图:

进入到.../systems/cortex_m0_mcu/verilog文件夹,除去tb_cmsdk_mcu.v、tbench_M0_DS.vc、cmsdk_uart_capture.v、cmsdk_clkreset外均用于本SoC的构建。其余文件功能描述如下(参考自DUI0926B 2.3.1):

  • cmsdk_mcu:示例SoC的顶层文件,包含SoC核心与存储器。
  • cmsdk_mcu_system:SoC核心,包含M0核心与CMSDK外设。
  • cmsdk_mcu_clkctrl:SoC的时钟与复位控制模块。
  • cmsdk_mcu_pin_mux:用于GPIO端口的功能复用,在输入、输出、复用三种功能内选择。
  • cmsdk_mcu_addr_decode:根据地址映射关系生成选择器信号。
  • cmsdk_mcu_defs:头文件,用于配置存储器的顺序与非顺序等待。
  • cmsdk_mcu_sysctrl:SoC的系统控制模块。
  • cmsdk_mcu_stclkctrl:SysTick信号的控制模块,用于生成系统嘀嗒信号。
  • cmsdk_ahb_cs_rom_table.v:CoreSight系统ROM表,用于调试。

准备完成MCU部分后,需要复制M0内核与CMSDK,通过顶层文件向下遍历需要的模块即可。该SoC主要包含如下内容:

  • M0内核(.../cores/cortexm0_designstart_r2p0/logical/):在DesignStart Kit中代码已被混淆,仅可使用SWD调试,AHB-Lite总线仅支持单主机。(参考自DUI0926B 1.3.2)
  • AHB与APB总线:AMBA定义的标准总线协议,APB主要用于挂在低速外设接口,二者通过AHB to APB桥进行转接。
  • RAM/ROM:FPGA内部均使用SRAM实现。
  • Timer:定时器,包括Simple Timer与Dual Timer。
  • UART:串口,用于数据交互。
  • Watchdog:看门狗,用于程序错误执行时的复位。

一个完整的设计包括有如下文件,注意cmsdk_fpga_rom模块同样使用SRAM实现,同时需修改入口参数为filename:

例化存储器

该SoC的存储器例化使用cmsdk_ahb_rom.v与cmsdk_ahb_ram.v,具体的文件结构如下:

cmsdk_ahb_rom模块的例化参数有:

  • MEM_TYPE:选择为2,意为AHB_ROM_FPGA_SRAM_MODEL。
  • AW:地址宽度,实际的存储器大小为2^AW。
  • filename:运行在M0上的程序镜像。
  • WS_N、WS_S:连续与非连续读写,意为Non-Sequential或Sequential。
  • BE:指定SoC程序的大小端,意为Big endian。

当RAM与ROM的大小均设置为16KB时,工程综合布局布线后的资源占用为(使用Vivado 2018.2,XC7Z010CLG400-1):

Post-Imple后资源占用

配置SWD

在该示例SoC中,默认关闭了SWD调试的电源,因此需要手动开启。在cmsdk_mcu_system.v中例化M0内核时,将调试线的REQ与ACK连接,如下:

wire debug_power_req_ack;
...
.CDBGPWRUPREQ  (debug_power_req_ack),
.CDBGPWRUPACK  (debug_power_req_ack),
...

新建顶层

由于需要添加加速器与时钟控制,因此新建顶层文件添加MMCM模块,注意在不同的平台上时钟IP核不同,需要额外修改。示例如下,本设计使用50M系统时钟:

module top(
    input in_clk,
    input in_rst_n,
    
    inout SWD,
    input SWCLK,
    input SWRST,
    
    inout [15:0] P0,
    inout [15:0] P1
);
wire sys_clk,sys_rst_n,mmcm_locked;
assign sys_rst_n = mmcm_locked & in_rst_n;
mmcm u_mmcm(
    // Clock out ports
    .clk_out1(sys_clk),     // output clk_out1
    // Status and control signals
    .resetn(in_rst_n), // input resetn
    .locked(mmcm_locked),       // output locked
    // Clock in ports
    .clk_in1(in_clk)    // input clk_in1
);      

parameter BE              = 0;   // Big or little endian
parameter BKPT            = 4;   // Number of breakpoint comparators
parameter DBG             = 1;   // Debug configuration
parameter NUMIRQ          = 32;  // NUM of IRQ
parameter SMUL            = 0;   // Multiplier configuration
parameter SYST            = 1;   // SysTick
parameter WIC             = 0;   // Wake-up interrupt controller support
parameter WICLINES        = 34;  // Supported WIC lines
parameter WPT             = 2;    // Number of DWT comparators
cmsdk_mcu #(
    .BE(BE),
    .BKPT(BKPT),
    .DBG(DBG),
    .NUMIRQ(NUMIRQ),
    .SMUL(SMUL),
    .SYST(SYST),
    .WIC(WIC),
    .WICLINES(WICLINES),
    .WPT(WPT)
) 
u_cmsdk_mcu(
    .XTAL1(sys_clk), // input
    .XTAL2(), // output
    .NRST(sys_rst_n),  // active low reset
    .P0(P0),
    .P1(P1),

    .nTRST(SWRST),
    .TDI(),
    .TDO(),
    .SWDIOTMS(SWD),
    .SWCLKTCK(SWCLK)
);
endmodule

调试

编写XDC约束文件,比特流生成完成后下载到FPGA开发板上,将调试器正确连接。注意,必须使用支持SWD的调试器,譬如ST-Link、CMSIS-DAP、Jink等。

在XADC中看到芯片温度有明显上升,一般可将此判断芯片成功下载的一个依据。

XADC温度上升
  • 连接调试端口SWD,SCLK,GND与串口UART0(用于打印调试信息),打开.../systems/cortex_m0_mcu/testcodes/hello下的Keil工程。

    注意在打开前,最好在Keil官网下载安装CMSIS的支持包,名称为Keil.V2M-MPS2_CMx_BSP.1.7.1.pack。

  • 修改内存地址,在Target选项卡的Memory Areas。我这里RAM/ROM均修改为16K,其中Start保持不变,改变Size即可。

    修改内存地址
  • 打开工程配置界面的Debug标签,选择正确的调试器,我这里选择的是CMSIS-DAP Debugger。

  • 在调试器Settings中将Port设置为SW,一切正常的话可以在SW Device中看到当前连接的设备。

  • 取消对Flash的操作,在FPGA内部对其操作并无太大意义,在此跳过烧录时的Flash操作。

    取消Flash操作
  • 点击Keil主界面上的Start/Stop Debug Session,由于还未添加Flash烧录算法此时会提示No Flash Operation,不影响运行与调试。

    Start/Stop Debug Session
  • 设置UartStdOutInit();函数,计算合适的BAUDDIV与CTRL。CMSIS外设均在DDI0479C文章中有所记载,下节将详述,本节给出50M下的配置代码。

    void UartStdOutInit(void)
    {
      CMSDK_UART0->BAUDDIV = 434; //The APB clock is 50M, the register is 50,000,000/115200 = 434
      CMSDK_UART0->CTRL    = 0x03; // Enable TX&RX
      CMSDK_GPIO1->ALTFUNCSET = (1<<1); // Port enable
      return;
    }
    
  • 打开串口调试助手,设置波特率到115200即可收到调试信息。

    Hello world from srT!

    其它

    在完成硬件移植并成功启动一起Hello World示例后,将会逐步移植Systick与GPIO,将其封装在库函数中供调用。读者可参考DDI0479C手册进行移植,下节将详细介绍。