ARM DesignStart M0 Yadan Board移植步骤
DesignStart是ARM的开源项目,开放了一系列SoC的IP,其中Cortex M0和Cortex M3提供相关的.v工程源码,让我们可以在自己的FPGA上实现移植与开发。
示例环境说明
软件:
Windows 11 Build 22581
TD 4.6.4
MDK 5.24.1
硬件:
Yadan Board
Jlink / ST-Link
获取源码
在官网上便可以直接通过自己的邮箱,注册并申请相关的开发者账号。
M0通常直接点击Apply Now便能直接下载。
除此之外,为了能够进行之后的嵌入式开发,我们还需要获取相应的外设RTL代码。以GPIO为例,这里我们使用的是平头哥(中天微C-SKY)过往开源计划里的GPIO IP(现在相关资源已经更新,但也可以直接下载使用)。相类似的也可以在opencores, asics.ws, github上获取到所需要的IP,或者自己根据需要进行开发。
新建工程
我们将下列.v RTL文件添加到我们的新工程中
ARM相关
AhbLitePC, cmsdk_ahb_bitband, cmsdk_ahb_cs_rom_table, cmsdk_ahb_default_slave, cmsdk_ahb_master_mux, cmsdk_ahb_memory_models_defs, cmsdk_ahb_ram_beh, cmsdk_ahb_rom, cmsdk_ahb_slave_mux, cmsdk_ahb_to_apb, cmsdk_ahb_to_iop, cmsdk_apb_slave_mux, cmsdk_apb_subsystem_m0ds, cmsdk_apb_uart, cmsdk_clkreset, cmsdk_iop_gpio, cmsdk_mcu_addr_decode, cmsdk_mcu_clkctrl, cmsdk_mcu_defs, cmsdk_mcu_pin_mux, cmsdk_mcu_stclkctrl, cmsdk_mcu_sysctrl, cmsdk_mcu_system, cmsdk_mcu, cortexm0ds_logic, CORTEXM0INTEGRATION,fpga_options_defs
其它
另外需要我们自己撰写或者添加的文件如下
顶层文件(自行撰写,引出所需要的引脚): M0demo
GPIO相关(可以自行撰写或着获取开源):gpio_apbif, gpio_ctrl, gpio
内存相关(标准AHB接口mem文件,可自行撰写): AHB2MEM
内核相关文件修改
以下修改内容不分先后顺序
cmsdk_apb_subsystem_m0ds.v
由于我们接入了我们自己的GPIO文件,所以需要将我们的外设接入到总线的分配系统上。我们是apb接口的GPIO,那么这里
1.将参数表(parameter)将原有的0号定时器(INCLUDE_APB_TIMER0)外设,修改为我们的GPIO(INCLUDE_APB_GPIO0)外设.
2.添加我们的gpio相关信号到定义
inout wire [7:0] b_pad_gpio_porta,
3.可以考虑注释掉没有使用到的多余的外设(例如uart,timer),减少综合时无用资源的消耗。
4.添加gpio中断过程信号
wire [7:0] gpio0_intr;
wire [7:0] i_gpio0_intr;
5.修改cmsdk_apb_slave_mux例化的外设,与第一步类似,将原有的TIMER0更改为我们的GPIO0
6.继续修改例化外设(cmsdk_apb_slave_mux)的相关信号,将原有的timer全部修改为我们的GPIO
.PSEL0 (gpio0_psel),
.PREADY0 (gpio0_pready),
.PRDATA0 (gpio0_prdata),
.PSLVERR0 (gpio0_pslverr),
7.例化我们的gpio
generate if (INCLUDE_APB_GPIO0 == 1) begin : gen_apb_gpio_0
gpio u_gpio_0(
.pclk(PCLK),
.presetn(PRESETn),
.psel(gpio0_psel),
.paddr(i_paddr[6:2]),
.penable(i_penable),
.pwrite(i_pwrite),
.pwdata(i_pwdata),
.prdata(gpio0_prdata),
.pready(gpio0_pready),
.pslverr(gpio0_pslverr),
.b_pad_gpio_porta(b_pad_gpio_porta[7:0]),
.pclk_intr(1'b1),
.gpio_intr(gpio0_intr)
);
end else
begin : gen_no_apb_gpio_0
assign gpio0_prdata = {32{1'b0}};
assign gpio0_pready = 1'b1;
assign gpio0_pslverr = 1'b0;
assign gpio0_intr = {8{1'b0}};
assign b_pad_gpio_porta = {8{1'b0}};
end endgenerate
8.在异步中断信号中增加gpoio相关信号赋值
generate if (INCLUDE_IRQ_SYNCHRONIZER == 0) begin : gen_irq_synchroniser
// If PCLK is synchronous to HCLK, no need to have synchronizers
assign i_uart0_txint = uart0_txint;
assign i_uart0_rxint = uart0_rxint;
/*
assign i_uart1_txint = uart1_txint;
assign i_uart1_rxint = uart1_rxint;
assign i_uart2_txint = uart2_txint;
assign i_uart2_rxint = uart2_rxint;
assign i_uart3_txint = uart3_txint; 8&^
assign i_uart3_rxint = uart3_rxint;
assign i_uart4_txint = uart4_txint;
assign i_uart4_rxint = uart4_rxint;
*/
assign i_gpio0_intr[7:0] = gpio0_intr[7:0];
assign i_timer1_int = timer1_int;
assign i_dualtimer2_int = dualtimer2_comb_int;
assign i_uart0_overflow_int = uart0_overflow_int;
/*
assign i_uart1_overflow_int = uart1_overflow_int;
assign i_uart2_overflow_int = uart2_overflow_int;
assign i_uart3_overflow_int = uart3_overflow_int;
assign i_uart4_overflow_int = uart4_overflow_int;
*/
assign i_watchdog_int = watchdog_int;
assign i_watchdog_rst = watchdog_rst;
end else
9.修改中断号分配,注释掉无用的信号,将其补0
assign apbsubsys_interrupt[31:0] = {
{8{1'b0}}, // 19-31 (AHB GPIO #0 individual interrupt)
i_gpio0_intr[7:0], // gpio
1'b0,
/*
i_uart4_txint, // 17
i_uart4_rxint, // 16
i_uart3_overflow_int, // 15
i_uart2_overflow_int, // 14
i_uart1_overflow_int, // 13
*/
i_uart0_overflow_int, // 12
1'b0, // 11
i_dualtimer2_int, // 10
i_timer1_int, // 9
1'b0, // 8
1'b0,
1'b0,
/*
i_uart3_txint, // 7
i_uart3_rxint, // 6
i_uart2_txint, // 5
i_uart2_rxint, // 4
i_uart1_txint, // 3
i_uart1_rxint, // 2
*/
i_uart0_txint, // 1
i_uart0_rxint};
9.注释或删除条件综合 `ifdef ARM_APB_ASSERT_ON后相关的语句
cmsdk_mcu_pin_mux.v
由于io本身需要通过三态门来实现,相对复杂,这里我们单独设置gpio为output功能,后期对应需要input功能的话只需要对应修改相关位置即可。
1.在信号定义中将P0的inout更改为output型
output wire [15:0] P0,
2.注释掉原本的P0输入赋值语句
//assign p0_in = P0;
3.注释所有P0的拉高操作(162行左右)
cmsdk_mcu_system.v
对mcu系统文件进行链接的修改,以正确链接我们添加的GPIO外设以及打开SWD调试功能
1.建议注释掉不需要的uart相关信号,减少例化的资源消耗
2.因为我们总线系统中是将原有的timer0替换掉了,因此这里对应替换相应的信号定义
inout [ 7:0] b_pad_gpio_porta,
//input wire timer0_extin,
3.添加SWD调试功能所需要的模块连接信号定义
// debug power control implementation
wire cpu0cdbgpwrupreq; // Debug Power Domain up request
wire cpu0cdbgpwrupack; // Debug Power Domain up acknowledge
assign cpu0cdbgpwrupack = cpu0cdbgpwrupreq;
4.修改cortex_m0_integration例化模块的连接信号
.CDBGPWRUPACK (cpu0cdbgpwrupack),
.CDBGPWRUPREQ (cpu0cdbgpwrupreq),
.STCALIB (26'h203d08f),
5.修改cmsdk_apb_subsystem_m0ds例化模块的传入参数,以保证正确的中断信号
cmsdk_apb_subsystem_m0ds #(
.APB_EXT_PORT12_ENABLE (0),
.APB_EXT_PORT13_ENABLE (0),
.APB_EXT_PORT14_ENABLE (0),
.APB_EXT_PORT15_ENABLE (INCLUDE_DMA),
.INCLUDE_IRQ_SYNCHRONIZER(0),
.INCLUDE_APB_TEST_SLAVE (0),
.INCLUDE_APB_GPIO0 (1), // Include simple timer #0
.INCLUDE_APB_TIMER1 (0), // Include simple timer #1
.INCLUDE_APB_DUALTIMER0 (0), // Include dual timer module
.INCLUDE_APB_UART0 (1), // Include simple UART #0
.INCLUDE_APB_UART1 (0), // Include simple UART #1
.INCLUDE_APB_UART2 (0), // Include simple UART #2.
.INCLUDE_APB_UART3 (0), // Include simple UART #3.
.INCLUDE_APB_UART4 (0), // Include simple UART #4.
.INCLUDE_APB_WATCHDOG (0), // Include APB watchdog module
.BE (BE)
)
6.添加相应的gpio例化信号在cmsdk_apb_subsystem_m0ds中
.b_pad_gpio_porta (b_pad_gpio_porta),
cmsdk_mcu.v
添加调试信号,以及所需的gpio信号到mcu配置文件
1.信号定义处添加调试所需的TMS和TCK信号,以及GPIO接口信号
inout wire TMS,
input wire TCK,
inout wire [7:0] b_pad_gpio_porta,
2.注释掉多余的uart连接wire信号,减少综合资源消耗
3.注释掉我们用于替换的timer0连接信号
//wire timer0_extin;
4.添加SWD调试所需要连接的wire信号,以及inout型的三态门
wire dbg_swdo_en; // SWD I/O 3-state enable
wire dbg_swdo; // SWD I/O 3-state output
assign TMS = dbg_swdo_en ? dbg_swdo : 1'bz;
5.修改cmsdk_mcu_system中的相关例化连接信号,替换timer0的例化添加gpio相关例化信号,并注释掉多余的uart例化信号
.SWDITMS (TMS),
.SWCLKTCK (TCK),
.SWDO (dbg_swdo),
.SWDOEN (dbg_swdo_en),
//GPIO
.b_pad_gpio_porta (b_pad_gpio_porta),
// Timer
.timer1_extin (timer1_extin),
6.将原有的Flash memory和SRAM相关的例化模块名修改为我们自行设计的mem的模块名,这里我们取名为AHB2MEM
7.注释掉cmsdk_mcu_pin_mux例化的uart相关信号
其它自行添加文件说明
M0demo.v
此为顶层文件,直接负责最上层用户所需要的少量信号
`timescale 1ns/1ps
`include "cmsdk_mcu_defs.v"
module M0demo(
input wire XTAL1, //
output wire XTAL2, //
input wire NRST, //
output wire [15:0] P0,
//inout wire [15:0] P1,
input wire nTRST,
input wire TDI,
output wire TDO,
inout wire SWDIOTMS,
input wire SWCLKTCK,
inout wire [7:0] b_pad_gpio_porta,
input wire uart0_rxd,
output wire uart0_txd,
output wire uart0_txen
);
wire XTAL1_wire;
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 = 1; // Wake-up interrupt controller support
parameter WICLINES = 34; // Supported WIC lines
parameter WPT = 2; // Number of DWT comparators
// --------------------------------------------------------------------------------
// Cortex-M0/Cortex-M0+ Microcontroller
// --------------------------------------------------------------------------------
cmsdk_mcu
#(.BE (BE),
.BKPT (BKPT), // Number of breakpoint comparators
.DBG (DBG), // Debug configuration
.NUMIRQ (NUMIRQ), // NUMIRQ
.SMUL (SMUL), // Multiplier configuration
.SYST (SYST), // SysTick
.WIC (WIC), // Wake-up interrupt controller support
.WICLINES (WICLINES), // Supported WIC lines
.WPT (WPT) // Number of DWT comparators
)
u_cmsdk_mcu (
.XTAL1 (XTAL1_wire), // input
.XTAL2 (XTAL2), // output
.NRST (NRST), // active low reset
.P0 (P0),
//.P1 (P1),
.nTRST (nTRST), // Not needed if serial-wire debug is used
.TDI (TDI), // Not needed if serial-wire debug is used
.TDO (TDO), // Not needed if serial-wire debug is used
.TMS (SWDIOTMS),
.TCK (SWCLKTCK),
.b_pad_gpio_porta (b_pad_gpio_porta),
.uart0_rxd (uart0_rxd),
.uart0_txd (uart0_txd),
.uart0_txen (uart0_txen)
);
M0clkpll u_M0clkpll(
.refclk (XTAL1 ),
.clk0_out (XTAL1_wire)
);
endmodule
AHB2MEM
这里是典型的amba总线协议接口的mem写法,不做特别说明
////////////////////////////////////////////////////////////////////////////////
// AHB-Lite Memory Module
////////////////////////////////////////////////////////////////////////////////
module AHB2MEM
#(parameter MEMWIDTH = 12) // Size = 4KB
(
input wire HSEL,
input wire HCLK,
input wire HRESETn,
input wire HREADY,
input wire [31:0] HADDR,
input wire [1:0] HTRANS,
input wire HWRITE,
input wire [2:0] HSIZE,
input wire [31:0] HWDATA,
output wire HREADYOUT,
output reg [31:0] HRDATA,
output wire HRESP
);
assign HREADYOUT = 1'b1; // Always ready
assign HRESP = 1'b0;
// Memory Array
reg [31:0] memory[0:(2**(MEMWIDTH-2)-1)];
// Registers to store Adress Phase Signals
reg [31:0] hwdata_mask;
reg we;
reg [31:0] buf_hwaddr;
// Sample the Address Phase
always @(posedge HCLK or negedge HRESETn)
begin
if(!HRESETn)
begin
we <= 1'b0;
buf_hwaddr <= 32'h0;
end
else
if(HREADY)
begin
we <= HSEL & HWRITE & HTRANS[1];
buf_hwaddr <= HADDR;
casez (HSIZE[1:0])
2'b1?: hwdata_mask <= 32'hFFFFFFFF; // Word write
2'b01: hwdata_mask <= (32'h0000FFFF << (16 * HADDR[1])); // Halfword write
2'b00: hwdata_mask <= (32'h000000FF << (8 * HADDR[1:0])); // Byte write
endcase
end
end
// Read and Write Memory
always @ (posedge HCLK)
begin
if(we)
memory[buf_hwaddr[MEMWIDTH:2]] <= (HWDATA & hwdata_mask) | (HRDATA & ~hwdata_mask);
HRDATA = memory[HADDR[MEMWIDTH:2]];
end
endmodule
PLL
通过IP生成添加相应的PLL文件,相关的例化见M0demo.v
至此就完成了整个RTL的文件修改添加对应所需要的引脚ADC约束进行综合即可
这里我们的ADC约束如下
set_pin_assignment { NRST } { LOCATION = T4; }
set_pin_assignment { SWCLKTCK } { LOCATION = A8; }
set_pin_assignment { SWDIOTMS } { LOCATION = A7; }
set_pin_assignment { XTAL1 } { LOCATION = K14; }
set_pin_assignment { XTAL2 } { LOCATION = A3; }
set_pin_assignment { b_pad_gpio_porta[0] } { LOCATION = N3; }
set_pin_assignment { nTRST } { LOCATION = R5; }
嵌入式工程软件配置
使用我们提供的MDK工程,进行相应的配置即可
如图所示,修改相应的指令存储与缓存的地址映射配置即可
测试验证
完成上述所有操作后,通过Jlink/ST-Link以SWD的连接方式进行连接。
首先在Debug中可以检测到相应的内核。
之后进行分步调试即可看见LED灯的闪烁