国产FPGA安路 高集成低功耗SF1系列FPSoC新品测评【使用SF1 RISCV ip核控制uart】
[复制链接]
本次我们测评的实验是用SF1的DEMO板设计 uart 通信实验:SF1的 fpga 部分调用 eMCU ip核中的uart接口,此部分用 TD 设计,在 FD 中使用 risc-v MCU 编写uart通信的c语言代码。最终实现DEMO板与电脑的串口通信。
1. 首先新建一个文件夹,再在里面新建 TD 、FD 的文件夹,分别用来保存 TD 软件和 FD 软件生成的文件。如下图。
2.打开 TD,New Project ,如下图
点击Tool里的 IP Generator ,选择 SF1 RISC-V ,勾选 UART Enable ,勾选三个gpio,勾选内部时钟使能,点击OK , 勾选位置后点击Yes,如下图
在软件左边的Project中将如下显示,双击uart_interface 即可打开生成的 ip 核代码,如下图
下面首先需要进行gpio的控制,主要就是输入输出方向的设置。新建 gpio_ctrl.v ,代码如下:
module gpio_ctrl(
input gpio_dir, //1'b0:input 1'b1:output
input gpio_out,
inout gpio, //对应fpga端口
output gpio_in
);
assign gpio = gpio_dir ? gpio_out:1'bz;
assign gpio_in = gpio; //当作为输入时,直接将输入值赋值给gpio_in
endmodule
接下来进行riscv ip核的控制,我们需要例化uart_interface.v 以及 gpio_ctrl.v 两个模块,在例化uart_interface.v中,core_clk 接口为MCU的输入时钟100MHz,timer_clk 为FPGA的时钟25MHz,需要注意的是riscv ip核的复位为高电平有效,例化代码如下:
module SF1_SoC(
input clk, //MCU的100MHz时钟
input rst, //active high
input timer_clk, //fpga的25MHz时钟
input jtag_tck,
output jtag_tdo,
input jtag_tms,
input jtag_tdi,
input uart_rx,
output uart_tx,
inout gpio_0,
inout gpio_1,
inout gpio_2
);
wire gpio0_out;
wire gpio1_out;
wire gpio2_out;
wire gpio0_dir;
wire gpio1_dir;
wire gpio2_dir;
wire gpio0_in;
wire gpio1_in;
wire gpio2_in;
gpio_ctrl u0_gpio_ctrl(
.gpio_dir(gpio0_dir), //1'b0:input 1'b1:output
.gpio_out(gpio0_out),
.gpio(gpio_0), //对应fpga端口
.gpio_in(gpio0_in)
);
gpio_ctrl u1_gpio_ctrl(
.gpio_dir(gpio1_dir), //1'b0:input 1'b1:output
.gpio_out(gpio1_out),
.gpio(gpio_1), //对应fpga端口
.gpio_in(gpio1_in)
);
gpio_ctrl u2_gpio_ctrl(
.gpio_dir(gpio2_dir), //1'b0:input 1'b1:output
.gpio_out(gpio2_out),
.gpio(gpio_2), //对应fpga端口
.gpio_in(gpio2_in)
);
uart_interface u0_uart_interface(
.soft_ip_apbm_en (1'b0),
.qspi0cfg1_mode (1'b1),
.qspi0cfg2_mode (1'b1),
.jtag_tck (jtag_tck),
.jtag_tdo (jtag_tdo),
.jtag_tms (jtag_tms),
.jtag_tdi (jtag_tdi),
.apb_clk ( ),
.apb_paddr ( ),
.apb_pwrite ( ),
.apb_penable ( ),
.apb_pprot ( ),
.apb_pstrobe ( ),
.apb_psel ( ),
.apb_pwdata ( ),
.apb_prdata ( ),
.apb_pready ( ),
.apb_pslverr ( ),
.uart_tx (uart_tx),
.uart_rx (uart_rx),
.gpio0_out (gpio0_out),
.gpio0_dir (gpio0_dir),
.gpio0_in (gpio0_in ),
.gpio1_out (gpio1_out),
.gpio1_dir (gpio1_dir),
.gpio1_in (gpio1_in ),
.gpio2_out (gpio2_out),
.gpio2_dir (gpio2_dir),
.gpio2_in (gpio2_in ),
.core_clk (clk ),
.timer_clk (timer_clk),
.core_reset (rst ),
.mtip ( ),
.nmi ( ),
.clic_irq ( ),
.sysrstreq ( ),
.apb_clk_down ( ),
.apb_paddr_down ( ),
.apb_penable_down ( ),
.apb_pprot_down ( ),
.apb_prdata_down ( ),
.apb_pready_down ( ),
.apb_pslverr_down ( ),
.apb_pstrobe_down ( ),
.apb_pwdata_down ( ),
.apb_pwrite_down ( ),
.apb_psel0_down ( ),
.apb_psel1_down ( ),
.apb_psel2_down ( )
);
endmodule
对于MCU的100MHz时钟,我们需要使用PLL ip核生成,其复位方式也是高电平复位。
之后新建工程的顶层文件 top.v,在里面例化各个子模块,需要注意的是需要手动设置 top.v 为顶层文件,代码如下:
module top(
input clk_25m,
input rst_n,
input jtag_tck,
input jtag_tms,
input jtag_tdi,
output jtag_tdo,
input uart_rx,
output uart_tx,
output led0,
output led1,
output led2
);
wire clk_100m;
wire rst;
assign rst = ~rst_n;
PLL u_PLL(
.refclk(clk_25m),
.reset(rst),
.clk0_out(clk_100m)
);
SF1_SoC u_SF1_SoC(
.clk(clk_100m), //MCU的100MHz时钟
.rst(rst),
.timer_clk(clk_25m), //fpga的25MHz时钟
.jtag_tck(jtag_tck),
.jtag_tdo(jtag_tdo),
.jtag_tms(jtag_tms),
.jtag_tdi(jtag_tdi),
.uart_rx(uart_rx),
.uart_tx(uart_tx),
.gpio_0(led0),
.gpio_1(led1),
.gpio_2(led2)
);
endmodule
之后进行引脚分配:
set_pin_assignment { clk_25m } { LOCATION = D7; }
set_pin_assignment { jtag_tck } { LOCATION = C7; }
set_pin_assignment { jtag_tdi } { LOCATION = D5; }
set_pin_assignment { jtag_tdo } { LOCATION = C6; }
set_pin_assignment { jtag_tms } { LOCATION = D6; }
set_pin_assignment { led0 } { LOCATION = J4; }
set_pin_assignment { led1 } { LOCATION = H5; }
set_pin_assignment { led2 } { LOCATION = J5; }
set_pin_assignment { rst_n } { LOCATION = H3; }
set_pin_assignment { uart_rx } { LOCATION = E4; }
set_pin_assignment { uart_tx } { LOCATION = A4; }
编译综合无误后TD部分完成。
下面进行FD的代码编写:
首先新建FD工程,选择uart_demo官方例程:
在例程中,我们做如下修改,程序的功能是在串口给板子发送 a 时,led0电平翻转,发送 b 时,led1电平翻转,发送 c时,led2电平翻转:
#include <stdio.h>
#include "nuclei_sdk_hal.h"
#include "nuclei_libopt.h"
void main(void)
{
char t;
uart_init(UART0, 115200,UART_BIT_LENGTH_8); //uart初始化,波特率115200,有效位八位
uart_config_stopbit(UART0,UART_STOP_BIT_1); //使用UART0,一位停止位
gpio_enable_output(GPIO,1 << 0); //使能三个gpio为输出模式
gpio_enable_output(GPIO,1 << 1);
gpio_enable_output(GPIO,1 << 2);
gpio_write(GPIO,1 << 0,1);
gpio_write(GPIO,1 << 1,1);
gpio_write(GPIO,1 << 2,1);
while(1)
{
t = uart_read(UART0);
if(t == 'a')
{
gpio_toggle(GPIO,1 << 0); //led0反转
}
if(t == 'b')
{
gpio_toggle(GPIO,1 << 1); //led1反转
}
if(t == 'c')
{
gpio_toggle(GPIO,1 << 2); //led2反转
}
}
}
在官方例程中默认的系统时钟是80MHz,我们需要将其修改为100MHz,如下,在system_nuclei.c中:
需要注意的是,官方的例程中uart波特率设置的时钟还在系统时钟的基础上进行了2分频,我们需在startup_anlogic.S 将0x83(2分频)修改为0x81(1分频)。
具体的寄存器配置方法请参考官方手册 TN817。
烧录程序之后显示CPU频率为100MHz,在分别发送 a b c 后,板子呈现的效果与预期相同。
对于一些寄存器以及时钟的配置还是需要仔细查阅手册,需要多一点耐心。
本次工程如下:
|