1768|1

3

帖子

0

资源

一粒金砂(中级)

[安路科技 SF1 系列 FPGA Rust 编程探索] - UART 初探 [复制链接]

本帖最后由 musiclee 于 2023-3-7 15:17 编辑

上一篇 [Rust on SF102] 安路科技 SF1 FPGA 系列评测 (一)- Hello rust! 尝试了针对安路科技 SF1 系列 FPGA 配置对应的 Rust 交叉编译环境,并编写了一个简单的 LED Blink 例程,并顺利地在SF102 DEMO 板上运行成功。接下来我们将正式创建一个 FPGA SOC 项目,用于评估后续的外设驱动。

FPGA SOC 项目

1. 创建项目 

1.png

 

2. 配置时钟,点选菜单 Tools -> IP Generator 生成锁相环:

2.png

 

点击 OK,并填写生成的目标文件名:

3.png

 

点击 OK,选择锁相环对应的 IP:

4.png

 

开发板输入时钟为 25MHz,将 PLL 配置为简单倍频到 100MHz 输出: 5.png

 

6.png

确认即可生成对应的 PLL IP 代码,软件会询问是否加入项目,确认选择加入。

 

3. 点选菜单 Tools -> IP Generator 生成 RISC-V 硬核 IP 代码

步骤和 PLL 相同,填写目标代码文件名:

7.png

 

IP 选择 SF1 RISCV:

8.png

 

当前我们仅验证 UART 编程,所以勾选最简配置:

9.png

确认生成并加入项目。至此完成必要的 IP 文件生成。

 

4. 编写主模块

主模块代码例化了 PLL 和 RISC-V 硬核,实现了支持 UART 的最小硬件系统:

`timescale 1ns/1ps

module SF1Simple (
  inout               led1,
  inout               led2,
  inout               led3,
  output              uart_tx,
  input               uart_rx,
  input               clk,
  input               reset
);

  wire                syspll_reset;
  wire                mcu_core_reset;
  wire                syspll_extlock;
  wire                syspll_clk0_out;
  wire       [31:0]   mcu_apb_prdata;
  wire                mcu_apb_pready;
  wire                mcu_apb_pslverr;
  wire                mcu_uart_tx;
  wire                mcu_gpio0_out;
  wire                mcu_gpio0_dir;
  wire                mcu_gpio1_out;
  wire                mcu_gpio1_dir;
  wire                mcu_gpio2_out;
  wire                mcu_gpio2_dir;
  wire                mcu_mtip;
  wire                mcu_sysrstreq;
  wire                mcu_apb_clk_down;
  wire       [31:0]   mcu_apb_pwdata_down;
  wire       [3:0]    mcu_apb_pstrobe_down;
  wire       [2:0]    mcu_apb_pprot_down;
  wire                mcu_apb_penable_down;
  wire                mcu_apb_pwrite_down;
  wire       [11:0]   mcu_apb_paddr_down;
  wire                mcu_apb_psel0_down;
  wire                mcu_apb_psel1_down;
  wire                mcu_apb_psel2_down;
  wire                gpio0_O_gpio_in;
  wire                gpio1_O_gpio_in;
  wire                gpio2_O_gpio_in;

  pll syspll (
    .refclk   (clk            ),
    .reset    (syspll_reset   ),
    .extlock  (syspll_extlock ),
    .clk0_out (syspll_clk0_out) 
  );
  
  rv32 mcu (
    .soft_ip_apbm_en  (1'b0                     ),
    .qspi0cfg1_mode   (1'b1                     ),
    .qspi0cfg2_mode   (1'b1                     ),
    .apb_clk          (1'b0                     ),
    .apb_paddr        (32'h0                    ),
    .apb_pwrite       (1'b0                     ),
    .apb_penable      (1'b0                     ),
    .apb_pprot        (3'b000                   ),
    .apb_pstrobe      (4'b0000                  ),
    .apb_psel         (1'b0                     ),
    .apb_pwdata       (32'h0                    ),
    .apb_prdata       (mcu_apb_prdata[31:0]     ),
    .apb_pready       (mcu_apb_pready           ),
    .apb_pslverr      (mcu_apb_pslverr          ),
    .uart_tx          (mcu_uart_tx              ),
    .uart_rx          (uart_rx                  ),
    .gpio0_in         (gpio0_O_gpio_in          ),
    .gpio0_out        (mcu_gpio0_out            ),
    .gpio0_dir        (mcu_gpio0_dir            ),
    .gpio1_in         (gpio1_O_gpio_in          ),
    .gpio1_out        (mcu_gpio1_out            ),
    .gpio1_dir        (mcu_gpio1_dir            ),
    .gpio2_in         (gpio2_O_gpio_in          ),
    .gpio2_out        (mcu_gpio2_out            ),
    .gpio2_dir        (mcu_gpio2_dir            ),
    .timer_clk        (syspll_clk0_out          ),
    .mtip             (mcu_mtip                 ),
    .core_clk         (syspll_clk0_out          ),
    .core_reset       (mcu_core_reset           ),
    .nmi              (1'b0                     ),
    .clic_irq         (16'h0                    ),
    .sysrstreq        (mcu_sysrstreq            ),
    .apb_clk_down     (mcu_apb_clk_down         ),
    .apb_prdata_down  (32'h0                    ),
    .apb_pwdata_down  (mcu_apb_pwdata_down[31:0]),
    .apb_pstrobe_down (mcu_apb_pstrobe_down[3:0]),
    .apb_pprot_down   (mcu_apb_pprot_down[2:0]  ),
    .apb_penable_down (mcu_apb_penable_down     ),
    .apb_pwrite_down  (mcu_apb_pwrite_down      ),
    .apb_pslverr_down (1'b0                     ),
    .apb_pready_down  (1'b0                     ),
    .apb_paddr_down   (mcu_apb_paddr_down[11:0] ),
    .apb_psel0_down   (mcu_apb_psel0_down       ),
    .apb_psel1_down   (mcu_apb_psel1_down       ),
    .apb_psel2_down   (mcu_apb_psel2_down       ) 
  );

  gpio_controler_2 gpio0 (
    .O_gpio_in  (gpio0_O_gpio_in),
    .I_gpio_dir (mcu_gpio0_dir  ),
    .I_gpio_out (mcu_gpio0_out  ),
    .IO_gpio    (led1           )
  );

  gpio_controler_2 gpio1 (
    .O_gpio_in  (gpio1_O_gpio_in),
    .I_gpio_dir (mcu_gpio1_dir  ),
    .I_gpio_out (mcu_gpio1_out  ),
    .IO_gpio    (led2           )
  );

  gpio_controler_2 gpio2 (
    .O_gpio_in  (gpio2_O_gpio_in),
    .I_gpio_dir (mcu_gpio2_dir  ),
    .I_gpio_out (mcu_gpio2_out  ),
    .IO_gpio    (led3           )
  );

  assign syspll_reset = (! reset);
  assign mcu_core_reset = (! reset);
  assign uart_tx = mcu_uart_tx;

endmodule

module gpio_controler_2 (
  output              O_gpio_in,
  input               I_gpio_dir,
  input               I_gpio_out,
  inout               IO_gpio
);

  assign IO_gpio = I_gpio_dir ? I_gpio_out : 1'bz;
  assign O_gpio_in = IO_gpio;

endmodule

 

5. 分配引脚

在软件界面左侧点击 IO Constraint ,为用到的端口分配引脚编号(参照原理图sf1s60cg121i_demo_board_v21.pdf)

10.png

 

 

生成的 .adc 文件内容如下(也可以直接编写 .adc 文件加入项目):

set_pin_assignment	{ clk }	{ LOCATION = D7; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ reset }	{ LOCATION = H3; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ uart_rx }	{ LOCATION = E4; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ uart_tx }	{ LOCATION = A4; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ led1 }	{ LOCATION = J4; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ led2 }	{ LOCATION = J5; IOSTANDARD = LVCMOS33; }
set_pin_assignment	{ led3 }	{ LOCATION = H5; IOSTANDARD = LVCMOS33; }

 

6. 综合布线

工具栏点击 run 按钮,正常编译通过:

11.png

至此 FPGA 项目设计完成。

编写 Rust 代码

在编写驱动 UART 的 Rust 代码之前,先简要介绍安路科技 SF1内嵌硬核的 UART 外设资源,SF1 支持一个名为 UART 0 的外设,对应的寄存器映射地址空间为 0xE001_0000 ~ 0x1001_FFFF,下面是其寄存器列表概览:

12.png

 

要驱动 UART 实现在串口终端输出字符串,最基本的步骤需要配置 UART_DIV、UART_TXCTRL 和 UART_SETUP 这三个寄存器。具体说明详见 TN817_SF1 MCU用户指南.pdf 。

Rust 开发环境以及项目创建指南已经在上一篇  [Rust on SF102] 安路科技 SF1 FPGA 系列评测 (一)- Hello rust! 中详细介绍,因此这里只展示主代码 main.rs:

#![no_std]
#![no_main]

extern crate riscv_rt;
extern crate panic_halt;
use riscv_rt::entry;
use core::ptr::{write_volatile,read_volatile};

// 时钟配置寄存器
const MISC_BASE_ADDRESS: u32 = 0xE0000000;
const CLOCKCTL_ADDRESS: u32 = MISC_BASE_ADDRESS + 0x10;
const CLOCKCTL: *mut u32 = CLOCKCTL_ADDRESS as *mut u32;

// 定义串口寄存器地址
const UART: u32 = 0xE001_0000;
const UART_TXDATA: u32 = UART + 0x00;
// const UART_RXDATA: u32 = UART + 0x04;
const UART_TXCTRL: u32 = UART + 0x08;
const UART_RXCTRL: u32 = UART + 0x0C;
const UART_IE: u32 = UART + 0x10;
// const UART_IP: u32 = UART + 0x14;
const UART_DIV: u32 = UART + 0x18;
// const UART_STATUS: u32 = UART + 0x1C;
const UART_SETUP: u32 = UART + 0x20;

// 定义串口波特率
const BAUD_RATE: u32 = 115200;

// 定义系统时钟频率
const SYSCLK: u32 = 50_000_000;

#[entry]
fn main() -> ! {
    // 初始化串口寄存器
    unsafe {
        write_volatile(CLOCKCTL, 0x83);

        // 设置波特率分频寄存器
        let div = SYSCLK / BAUD_RATE - 1;
        write_volatile(UART_DIV as *mut u32, div);

        // 设置发送控制寄存器,使能发送功能和中断
        let txctrl = read_volatile(UART_TXCTRL as *mut u32) | 0x1;
        write_volatile(UART_TXCTRL as *mut u32, txctrl);

        // 设置接收控制寄存器,使能接收功能和中断
        let rxctrl = read_volatile(UART_RXCTRL as *mut u32) | 0x1;
        write_volatile(UART_RXCTRL as *mut u32, rxctrl);

        write_volatile(UART_SETUP as *mut u32, 0x30);

        // 设置中断使能寄存器,使能发送就绪和接收就绪中断
        let ie = (1 << 0) | (1 << 1);
        write_volatile(UART_IE as *mut u32, ie);
    }

    // 向串口输出 "Hello world." 字符串
    let message = b"Hello world.\r\n";
    for &byte in message.iter() {
        send_byte(byte);
    }

    loop {}
}

// 定义一个函数,用于发送一个字节到串口
fn send_byte(byte: u8) {
    unsafe {
        // 等待发送数据缓冲区为空
        while read_volatile(UART_TXDATA as *const u32) & (1 << 31) != 0 {}
        // 写入数据到发送数据缓冲区
        write_volatile(UART_TXDATA as *mut u32, byte as u32);
    }
}

 

以下是 Cargo 项目的配置文件和链接文件,创建 Rust 项目请参考第一篇 [Rust on SF102] 安路科技 SF1 FPGA 系列评测 (一)- Hello rust!

 

cargo/config 文件

[target.riscv32imac-unknown-none-elf]

rustflags = [
  "-C", "link-arg=-Tmemory.x",
  "-C", "link-arg=-Tlink.x"
]

[build]
target = "riscv32imac-unknown-none-elf"

 

memory.x 文件

MEMORY
{
  flash : ORIGIN = 0x000E0000, LENGTH = 4M 
  ilm : ORIGIN = 0x08000000, LENGTH = 8K  
  ram : ORIGIN = 0x20000000, LENGTH = 8K
}

REGION_ALIAS("REGION_TEXT", flash);
REGION_ALIAS("REGION_RODATA", flash);
REGION_ALIAS("REGION_DATA", ram);
REGION_ALIAS("REGION_BSS", ram);
REGION_ALIAS("REGION_HEAP", ram);
REGION_ALIAS("REGION_STACK", ram);

 

编译并生成 hex 文件

cargo build --release
cargo objcopy --release -- -O ihex oledtest.hex

烧写运行

首先需要连接好主板 USB 和 下载器,如图 Type-C 接口会在电脑上识别为一个USB串口设备

13.png

 

打开对应的串口终端,设置波特率为 115200, 具体参数如下:

14.png

 

然后在 TD 软件下载 FPGA(具体步骤参考 [Rust on SF102] 安路科技 SF1 FPGA 系列评测 (一)- Hello rust! ):

15.png

 

下载成功后,将会在串口终端看到输出信息:

16.png

 

至此,UART 驱动成功!

FPGA 项目文件: td2.zip (315.37 KB, 下载次数: 1)

最新回复

学习学习了  详情 回复 发表于 2023-3-7 23:51

回复

2

帖子

0

资源

一粒金砂(初级)

学习学习了

回复
您需要登录后才可以回帖 登录 | 注册

相关帖子
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
混合信号技术在汽车电子单芯片中的方案

1、电压的变化范围过大 电网供电不足,供电部门采取降压供电,或地处偏远地带,损耗过多,导致电压偏低。电网用电太少,导致 ...

TL431电路的应用专题

蒙soso厚爱,在此向电路新手介绍一个tl431芯片的应用。这颗芯片本身是一个并联基准芯片,由于检测端的存在,可以衍生出很多应用 ...

Helper2416助学: 玩转ARM应用程序开发从QT开始

写在前面的废话: 今天终于有空了,现把前段时间玩ARM Linux上的应用开发学习过程与大家分享,也算是对这一阶段学习过程的总结 ...

adafruit的micropython液晶驱动

adafruit分享了多个RGB液晶等驱动,大家有空可以研究一下。包括了: hx8353 ili9341 ssd1331 ssd1351 st7735 等多种型 ...

开关电源各处损耗探究

概述能量转换系统必定存在能耗,虽然实际应用中无法获得100%的转换效率,但是,一个高质量的电源效率可以达到非常高的水平,效率 ...

关于用dmax的spi从模式进行传输的问题

芯片使用的是TI的tms320c6726b芯片,在使用dmax作为spi从模式进行传输过程中,发现进不去dmax的传输事件中断,现将程序表述如下 ...

I2C总线接上拉电阻的原因

I2C为什么要接上拉电阻?因为它是开漏输出。 为什么是开漏输出? I2C协议支持多个主设备与多个从设备在一条总线上,如 ...

【米尔MYC-J1028X开发板试用】搭建DLNA流媒体服务器

在上一篇文章中,我分享了使用Transmission 来建立PT高速下载服务器,可以用来进行媒体资源的下载。 成功下载媒体资源后,我 ...

嵌入式Qt-简易网络监控摄像头

本编利用Qt实现一个网络摄像头功能,包含一个服务端和一个客户端,服务端用于将USB摄像头转换为一个IP摄像头,当有客户端 ...

玩转RP2040之Python开发环境搭建

上篇文章:玩转RP2040之开箱测评与上电运行,介绍了RP2040的硬件和上电使用情况,本篇来进行软件开发环境的搭建。 ...

关闭
站长推荐上一条 1/9 下一条
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2023 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表