文章目录
- @[toc]
- 1. ARM核定制
- 2. ARM核程序设计
- 3. ARM程序烧写
- 4. 工程下载
文章目录
- @[toc]
- 1. ARM核定制
- 2. ARM核程序设计
- 3. ARM程序烧写
- 4. 工程下载
本文是高云FPGA系列教程的第5篇文章。
前面几篇笔记都是介绍的高云GW1NSR-4C FPGA部分的使用,本篇文章介绍片上ARM Cortex-M3硬核处理器的使用,演示如何定制一颗ARM处理器硬件,ARM核程序设计及下载。
关于在FPGA上实现硬核和软核处理器,感兴趣的朋友可以查看之前写的几篇文章:
-
FPGA硬核和软核处理器的区别
https://mp.weixin.qq.com/s/Y2cHZ6VYFngVPwO9upr69g
-
有哪些内嵌ARM硬核的FPGA?
https://mp.weixin.qq.com/s/C7y9y5XCid3HxK6yKdYVwg
-
手把手教你在FPGA上定制一颗ARM处理器
https://mp.weixin.qq.com/s/ZbNZCf-NW00vxuHSFvAoow
1. ARM核定制
高云GW1NSR-4C硬核处理器,基于ARM Cortex-M3 32Bit RISC内核,ARM3v7M 架构,最高80MHz工作频率,STM32单片机一般最高是72MHz,而Xilinx Microblaze处理器最高可跑到上百兆,而Microchip的ARM硬核也能跑到100多兆,GW1NSR-4C处理器的主频算是一般速度,不过足够应付一般的应用场景了。
首先打开一个基本的FPGA工程,比如LED点灯工程,打开IP核生成工具,选择IP核保存的路径,模块名称。
双击框图中的GPIO模块,使能GPIO
使能串口0
生成的例化模板:
Gowin_EMPU_Top your_instance_name(
.sys_clk(sys_clk_i), //input sys_clk
.gpioin(gpioin_i), //input [15:0] gpioin
.gpioout(gpioout_o), //output [15:0] gpioout
.gpioouten(gpioouten_o), //output [15:0] gpioouten
.uart0_rxd(uart0_rxd_i), //input uart0_rxd
.uart0_txd(uart0_txd_o), //output uart0_txd
.reset_n(reset_n_i) //input reset_n
);
例化到顶层模块中:
/***************************************************************
* Copyright(C), 2010-2022, WeChat:MCU149.
* ModuleName : top_hdl.v
* Date : 2022年9月27日
* Time : 20:19:39
* Author : WeChat:MCU149
* Function : gw1nsr-4c led driver demo
* Version : v1.0
* Version | Modify
* ----------------------------------
* v1.0 .....
***************************************************************/
module top_hdl(
//Inputs
input gclk, // 27MHz
input gresetn,
input key,
input uart_rxd,
//Outputs
output uart_txd,
output led
);
wire clk_60m;
wire arm_clk = clk_60m;
wire arm_resetn = gresetn;
wire arm_uart0_rxd = uart_rxd;
wire arm_uart0_txd;
wire [15:0] arm_gpio_in;
wire [15:0] arm_gpio_out;
wire [15:0] arm_gpio_outen;
assign uart_txd = arm_uart0_txd;
assign led = (arm_gpio_out[15:0] == 16'haaaa);
assign arm_gpio_in[15:0] = {16{key}};
Gowin_PLLVR pll_ut0(
.clkout(clk_60m), //output clkout
.clkin(gclk) //input clkin
);
Gowin_EMPU_Top arm_cortex_m3_core(
//Inputs
.sys_clk(arm_clk),
.reset_n(arm_resetn),
.uart0_rxd(arm_uart0_rxd),
.gpioin(arm_gpio_in[15:0]),
//Outputs
.uart0_txd(arm_uart0_txd),
.gpioout(arm_gpio_out[15:0]),
.gpioouten(arm_gpio_outen[15:0])
);
endmodule //top_hdl end
模块的功能为,将按键状态连接到GPIO输入,GPIO输出连接到led,ARM核的主频为60MHz,来自PLL。
2. ARM核程序设计
GW1NSR-4C ARM核程序支持高云官方GMD环境,基于Eclipse开源框架,配合arm-gcc编译器来完成ARM核程序编写、编译、调试、下载等。还支持常用的单片机开发环境Keil-MDK,芯片型号选择通用的ARM-CM3处理器。
官方提供了非常不错的参考设计,包括FPGA工程,GMD工程,Keil工程,
下载地址:Gowin_EMPU_V1.0.zip
http://cdn.gowinsemi.com.cn/Gowin_EMPU_V1.0.zip
还有非常详细的IDE软件使用手册:IPUG928-1.1_Gowin_EMPU(GW1NS-4C)_IDE软件参考手册.pdf
http://cdn.gowinsemi.com.cn/IPUG928-1.1_Gowin_EMPU(GW1NS-4C)_IDE%E8%BD%AF%E4%BB%B6%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C.pdf
官方的参考设计基于DK-START开发板,包括FPGA工程和ARM工程(Keil+GMD),涵盖了所有外设的实例,包括固件库,参考设计,AHB2,APB2,I2C,SPI,UART,TIMER,INTC,WDOG,RTC,FreeRTOS,UCOS_II等等
文末有本次示例的工程下载地址,配套TangNano 4K开发板。
首先修改ARM程序中的系统主频,位于system_gw1ns4c.c文件中,
/*----------------------------------------------------------------------------
Define clocks
*----------------------------------------------------------------------------*/
#define __XTAL (120000000UL) /* Oscillator frequency */
#define __SYSTEM_CLOCK (__XTAL / 2) /* 60MHz */
和FPGA工程中给定的时钟频率保持一致,否则延时时间不准确,串口输出为乱码。
下面来介绍几个函数的实现。
首先是延时函数实现,基于系统SysTick定时器实现。
需要在gw1ns4c_it.c文件中将SysTick_Handler函数删除,或者添加弱定义关键字,以免和用户的中断函数冲突。
/**
* @brief This function handles SysTick Handler.
* @param none
* @retval none
*/
__weak void SysTick_Handler(void)
{
}
delay.h文件内容
#include "gw1ns4c.h"
void delay_init(void);
void SysTick_Handler(void);
void delay_us(uint32_t nus);
void delay_ms(uint32_t nms);
delay.c文件内容
#include "drv_timer.h"
uint32_t fac_us=0; //us延时倍乘数
uint32_t fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
void delay_init(void)
{
SystemInit();
SystemCoreClockUpdate(); //可以省略
}
void SysTick_Handler(void)
{
if(fac_us) fac_us--;
if(fac_ms) fac_ms--;
}
void delay_us(uint32_t nus)
{
SysTick_Config(SystemCoreClock / 1000000); //定时1us
fac_us = nus;
while(fac_us != 0);
}
void delay_ms(uint32_t nms)
{
SysTick_Config(SystemCoreClock / 1000); //定时1ms
fac_ms = nms;
while(fac_ms != 0);
}
printf重定向到串口。
Keil环境下:
#include "gw1ns4c.h"
#include <stdio.h>
int fputc(int ch, FILE *f)
{
UART_SendChar(UART0, (unsigned char) ch);
while(UART0->STATE & UART_STATE_TXBF);//UART0
return (ch);
}
int fgetc(FILE *f)
{
while(!(UART0->STATE & UART_STATE_RXBF));//UART0
return (int)UART_ReceiveChar(UART0);
}
需要同时勾选使用MicroLib,就可以使用printf函数了。
高云官方GMD环境:
#include "gw1ns4c.h"
#include <stdio.h>
#include <sys/stat.h>
__attribute__ ((used)) int _write (int fd, char *ptr, int len)
{
size_t i;
for (i=0; i<len; i++)
{
UART_SendChar(UART0,ptr[i]); // call character output function
}
return len;
}
最后主函数设计:
#include "main.h"
int main(void)
{
uint16_t rd = 0;
uint16_t period = 100;
delay_init();
uart0_init(115200);
printf("SystemCoreClock = %d\r\n", SystemCoreClock);
printf("Hello GW1NSR-4C SoC(ARM Cortex-M3)\r\n");
// GPIO_SetOutEnable(GPIO0, 0xffff); //0=in, 1=out
while(1)
{
gpio_read(&rd); //read gpio0
// if(((rd >> 8) & 1) == 0)//key press
if(rd == 0)//key press
{
printf("press: ");
period = 100;
}
else if(rd == 0xffff) //key release
{
printf("release: ");
period = 500;
}
printf("GW1NSR-4C ARM Cortex-M3 (Keil-MDK)\r\n");
gpio_write(0xaaaa);
delay_ms(period);
gpio_write(0x0000);
delay_ms(period);
}
}
主函数的功能为,当按键未按下时,板载LED 500ms翻转一次,同时串口输出Release字符串,按键按下时,板载LED 100ms翻转一次,同时输出press字符串。
编程生成bin格式固件,GMD环境生成路径为:
Keil环境下,需要添加编译后执行fromelf命令,将axf文件转换为bin文件。
fromelf --bin -o "$L@L.bin" "#L"
配置如下图:
3. ARM程序烧写
ARM程序的烧写也比较简单,可以使用FPGA的编程工具,配置如下图所示:
fs文件选择对应的FPGA工程生成的比特流文件,bin文件为ARM核程序。
4. 工程下载
TangNano 4K配套工程下载
- gw1nsr_4c_mcu_demo.rar
本文是高云FPGA系列教程的第5篇文章。