安路FPGA移植Cortex-M0内核

        本文是关于基于安路FPGA EG4S20BG256移植Cortex M0内核的笔记。硬件平台使用硬木课堂的安路核心板,软件使用安路的TD5.6.2和keil5。(博主刚学FPGA不久,文中有不足之处请帮忙指出)

        在移植之前,博主看了网上很多的文章、教程,感觉没有找到一篇手把手从头到尾移植的教程,或者缺少RAM IP核移植,或者缺少SWD、JTAG的引出,最后是结合了各家的代码和教程,以及ARM官方给的示例,移植完了M0内核。

        在移植之前,笔者已经学习了AHB总线、ARM内核、单片机、FPGA verilog基础语法部分。


一、准备部分

1.1 DesignStart计划

        ARM官方的DesignStart计划提供了Cortex-M0、Cortex-M3、Cortex-A5等内核,每一种IP也有不同的版本:Eval版,FPGA版,Pro版。其中Eval版本提供处理器核的网表形式的Verilog代码。

        进入网站:https://developer.arm.com/,搜索“Cortex-M0 Designstart Eval”,就可以找到Eval版的资源。下载资源需要登录ARM账号。

        如果注册ARM账号太麻烦,也可以在优快云等其它平台上下载。

1.2 TD和Keil5准备

        安路FPGA开发需要使用安路官方的TD(TangDynasty),笔者使用的是TD5.6.2,好像TD不同版本支持的芯片是不一样,读者可以认真阅读以下安路官网。

        至于Keil5笔者就不再赘述了,开发ARM内核的必备,网上也有很多资源了。

        下载了TD还需要下载TD的License,注册以后免费下载。


二、解析ARM DesignStart

        解压之后,里面有文档说明,建议读者详细阅读ARM官方给的文档,里面说的很详细,虽然是全英文,可以带着翻译慢慢读,这对后面移植还是很有帮助的。

        documentation/arm_cortex_m0_designstart_eval_user_guide.pdf里第14页,1.2节有关于Cortex-M0 DesignStart Eval的目录结构说明。

        打开systems/cortex_m0_mcu/verilog/cmsdk_mcu.v,然后根据cmsdk_mcu.v中的模块,层层梳理,可以得到下图所示的结构。

将cmsdk_mcu.v看作一个顶层文件。

  • AHB_RAM和AHB_ROM,即RAM和ROM模块。
  • cmsdk_mcu_clkctrl:时钟控制模块。
  • cmsdk_mcu_pin_mux:引脚复用模块。
  • cmsdk_mcu_system:Cortex-M0系统的系统级设计。
    • cmsdk_mcu_addr_decode:AHB地址解码。
    • cmsdk_ahb_slave_mux:AHB从多路复用器。
    • cmsdk_ahb_default_slave:AHB总线默认从属设备,当被选择传输时返回error。
    • CORTEXM0INTEGRATION:Cortex-M0 DesignStart processor 集成级。
      • Cortexm0ds_logic:Cortex-M0 DesignStart processor 逻辑级。
    • cmsdk_ahb_cs_rom_table:该模块实现了一个通用的4入口的AHB CoreSight ROM Table。
    • cmsdk_mcu_sysctrl:系统控制器。
    • cmsdk_ahb_gpio:IOP GPIO外设。
    • cmsdk_mcu_stclkctrl:系统嘀嗒信号控制。
    • cmsdk_apb_subsystem:APB子系统
      • cmsdk_apb_slave_mux:APB从多路复用器
      • cmsdk_ahb_to_apb:AHB-APB桥
      • cmsdk_apb_timer:定时器
      • cmsdk_apb_dualtimers:两个32位向下计数器
      • cmsdk_apb_watchdog:看门狗
      • cmsdk_apb_uart:串口
      • cmsdk_apb_test_slave:在APB子系统中测试等待状态和error回应的从设备。

三、移植

3.1 导入文件

        根据第二章的结构分析,将下列文件从Cortex-M0 DesignStart中找到,添加到工程中

  1. cores/cortexm0_designstart_r2p0/logical/cortexm0_integration/verilog/cortexm0ds_logic.v
  2. cores/cortexm0_designstart_r2p0/logical/cortexm0_integration/verilog/CORTEXM0INTEGRATION.v
  3. systems/cortex_m0_mcu/cmsdk_ahb_cs_rom_table.v
  4. systems/cortex_m0_mcu/cmsdk_mcu.v
  5. systems/cortex_m0_mcu/cmsdk_mcu_addr_decode.v
  6. systems/cortex_m0_mcu/cmsdk_mcu_clkctrl.v
  7. systems/cortex_m0_mcu/cmsdk_mcu_defs.v
  8. systems/cortex_m0_mcu/cmsdk_mcu_pin_mux.v
  9. systems/cortex_m0_mcu/cmsdk_mcu_stclkctrl.v
  10. systems/cortex_m0_mcu/cmsdk_mcu_sysctrl.v
  11. systems/cortex_m0_mcu/cmsdk_mcu_system.v
  12. logic/cmsdk_ahb_default_slave/cmsdk_ahb_default_slave.v
  13. logic/cmsdk_ahb_gpio/cmsdk_ahb_gpio.v
  14. logic/cmsdk_ahb_gpio/cmsdk_ahb_to_iop.v
  15. logic/cmsdk_ahb_slave_mux/cmsdk_ahb_slave_mux.v
  16. logic/cmsdk_ahb_to_apb/cmsdk_ahb_to_apb.v
  17. logic/cmsdk_apb_dualtimers/cmsdk_apb_dualtimers.v
  18. logic/cmsdk_apb_dualtimers/cmsdk_apb_dualtimers_defs.v
  19. logic/cmsdk_apb_dualtimers/cmsdk_apb_dualtimers_frc.v
  20. logic/cmsdk_apb_slave_mux/cmsdk_apb_slave_mux.v
  21. logic/cmsdk_apb_subsystem/cmsdk_apb_subsystem.v
  22. logic/cmsdk_apb_subsystem/cmsdk_apb_test_slave.v
  23. logic/cmsdk_apb_timer/cmsdk_apb_timer.v
  24. logic/cmsdk_apb_uart/cmsdk_apb_uart.v
  25. logic/cmsdk_apb_watchdog/cmsdk_apb_watchdog.v
  26. logic/cmsdk_apb_watchdog/cmsdk_apb_watchdog_defs.v
  27. logic/cmsdk_apb_watchdog/cmsdk_apb_watchdog_frc.v
  28. logic/cmsdk_iop_gpio/cmsdk_iop_gpio.v
  29. logic/models/memories/cmsdk_ahb_memory_models_defs.v
  30. logic/models/memories/cmsdk_ahb_ram.v
  31. logic/models/memories/cmsdk_ahb_ram_beh.v
  32. logic/models/memories/cmsdk_ahb_rom.v

        以上共计32个文件,加入到工程中,将cmsdk_mcu.v设置为顶层文件,然后直接进行编译。右键点击cmsdk_mcu->Set As Top。

        控制台会报错误,循环次数不能大于10000,这是由于cmsdk默认设置的RAM和ROM时64KB,而这样建立工程的时候没有使用FPGA的RAM IP核,占用了大量的寄存器和逻辑单元。

3.2 修改RAM和ROM大小

        在cmsdk_mcu.v文件里,找到cmsdk_ahb_rom和cmsdk_ahb_ram两个模块,将参数AW从16修改为8。并将cmsdk_ahb_rom里的filename参数给删除,如果需要烧录映像,可以将hex文件添加,这里暂时删除。

        重新进行编译,这里就已经能够顺利编译完成了。

3.3 Keil读取内核

        在能够顺利编译之后,根据引脚,自行分配SWD引脚,重新编译,并下载到开发板中,连接DAP仿真器,用Keil创建一个ARM CM0的工程,打开创建好的KEIL工程。

        先在小魔法棒中检查是否能读取到ARM内核。如果能够成功读取到IDCODE,并且读取到ARM CoreSight SW-DP,表明内核已经可以工作了。

3.4 激活SWD   

        此时,还不能正常使用调试功能,需要在内核中将调试功能打开。找到cmsdk_mcu_system.v,找到278行附近:

        wire              EDBGRQ;

        在后面添加以下代码:

wire      cdbgpwrupack_wire;
wire      cdbgpwrupreq_wire;
assign    cdbgpwrupack_wire = cdbgpwrupreq_wire;

         然后找到CORTEXM0INTEGRATION模块,POWER MANAGEMENT里的CDBGPWRUPREQ和CDBGPWRUPACK,分别填入cdbgpwrupreq_wire和cdbgpwrupack_wire。

        重新编译,下载。

3.5 Keil进行内存调试

        打开KEIL工程,取消勾选魔法棒->Debug->Load Application at Startup,取消勾选魔法棒->Debug->Settings->Reset after Cornnect,取消勾选魔法棒->Utilities->Update Target before Debugging。

        点击调试。点击工具栏->View->Memory Windows->Memory 1。在内存窗口中,输入地址0x20000000,按下回车键,然后随意修改任意地址,观察是否能成功修改内存的值。如果能够成功修改,说明RAM和ROM是没有问题的。

四、RAM和ROM

4.1 自动RAM和ROM

        ARM官方的代码生成的RAM和ROM不能够太大(笔者能力有限),笔者从网上抄了一份RAM的代码,由于时间久远,忘记出处了,无法标记原作者。


// AHB-Lite Memory Module

module EG4_AHBLite_AutoRAM
   #(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

4.2 IP核生成RAM和ROM

        笔者尝试了自己使用安路TD的IP generate生成一个RAM IP核,然后接入到AHB总线中,代码如下,仅供参考



// AHB-Lite Memory Module

module EG4_AHBLite_BRAM
(
	input wire           HCLK,
	input wire           HRESETn,
	input wire           HSEL,
	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 wire   [31:0] HRDATA,
	output wire          HRESP
);

	assign HREADYOUT = 1'b1;
	assign HRESP     = 1'b0;

	reg	[31:0]	buf_hwaddr;
	reg			ram_cs;
	reg	[3:0]	ram_we;

	wire [31:0]	buf_hraddr;
	assign buf_hraddr = HADDR>>2;

	always @(posedge HCLK or negedge HRESETn) begin
		if (!HRESETn) begin
			buf_hwaddr <= 32'd0;
			ram_cs <= 1'b0;
			ram_we <= 4'd0;
		end
		else begin
			buf_hwaddr <= HADDR>>2;
			ram_cs <= HSEL & HWRITE & HTRANS[1];
			if (HWRITE) begin	
				casez (HSIZE[1:0])
            		2'b1?: ram_we <= 4'b1111;						// Word write
            		2'b01: ram_we <= (4'b0011<< (HADDR[1]<<1));		// Halfword write
            		2'b00: ram_we <= (4'b0001<< HADDR[1:0]);		// Byte write
            	endcase
			end
			else begin
				ram_we <= 4'd0;
				ram_cs <= 1'b0;
			end
		end
	end


	mcu_ram u_mcu_ram
	(		
		.clka		(HCLK),				// clock
		.cea		(ram_cs),			// clock enable,active High
		// .rsta		(1'b0),			// reset,active High
		.addra		(buf_hwaddr),		// address input
		.dia		(HWDATA),			// data input
		.wea		(ram_we),			// write byte
		
		.clkb		(HCLK),
		.ceb		(1'b1),
		// .rstb		(1'b0),			// reset,active High
		.addrb		(buf_hraddr),		// address input
		.dob		(HRDATA)			// data output
		// .oceb		(1'b1) 			// data output register enable
	);

	// parameter DATA_WIDTH_A = 32; 
	// parameter ADDR_WIDTH_A = 12;
	// parameter DATA_DEPTH_A = 4096;
	// parameter DATA_WIDTH_B = 32;
	// parameter ADDR_WIDTH_B = 12;
	// parameter DATA_DEPTH_B = 4096;
	// parameter REGMODE_A    = "NOREG";
	// parameter REGMODE_B    = "NOREG";
	// parameter WRITEMODE_A  = "NORMAL";
	// parameter WRITEMODE_B  = "NORMAL";

	// EG_LOGIC_BRAM #( .DATA_WIDTH_A(DATA_WIDTH_A),
	// 			.DATA_WIDTH_B(DATA_WIDTH_B),
	// 			.ADDR_WIDTH_A(ADDR_WIDTH_A),
	// 			.ADDR_WIDTH_B(ADDR_WIDTH_B),
	// 			.DATA_DEPTH_A(DATA_DEPTH_A),
	// 			.DATA_DEPTH_B(DATA_DEPTH_B),
	// 			.BYTE_ENABLE(8),
	// 			.BYTE_A(4),
	// 			.BYTE_B(4),
	// 			.MODE("PDPW"),
	// 			.REGMODE_A(REGMODE_A),
	// 			.REGMODE_B(REGMODE_B),
	// 			.WRITEMODE_A(WRITEMODE_A),
	// 			.WRITEMODE_B(WRITEMODE_B),
	// 			.RESETMODE("SYNC"),
	// 			.IMPLEMENT("9K"),
	// 			.INIT_FILE("NONE"),
	// 			.FILL_ALL("NONE"))
	// 		inst(
	// 			.dia(HWDATA),
	// 			.dib({32{1'b0}}),
	// 			.addra(buf_hwaddr),
	// 			.addrb(buf_hraddr),
	// 			.cea(ram_cs),
	// 			.ceb(1'b1),
	// 			.ocea(1'b0),
	// 			.oceb(1'b0),
	// 			.clka(HCLK),
	// 			.clkb(HCLK),
	// 			.wea(1'b0),
	// 			.bea(ram_we),
	// 			.web(1'b0),
	// 			.beb(2'b00),
	// 			.rsta(1'b0),
	// 			.rstb(1'b0),
	// 			.doa(),
	// 			.dob(HRDATA));

endmodule

五、结束

        至此,已经能够跑通M0内核,并且能够下载程序和运行了,关于代码里带有的Uart、TIM定时器等外设,读者可以自行参考ARM官方的案例和手册。

        关于自己写APB、AHB外设,读者可以参考ARM官方给的各种外设,是如何挂到总线上,如何例化,如何读写寄存器、中断等,ARM官方给的示例非常丰富,值得仔细学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天师电通电容爆破工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值