非常感谢有机会接触澎峰科技出品的Perf-V开发板,让我有机会接触和探究RISC-V软核
。玩Perf-V开发板的都会发现,其板载的4个LED灯中只有LED3能被用户程序访问操作,也就是用户无法完成最基本的入门程序--流水灯实验(就和学程序设计语言一样,helloworld程序一样)。所以经过一番折腾(看部分源码、看胡老师的两本书和自己对fpga的一些认识),总算将E203 SOC的I/O引脚进行了重新映射,使得在Perf-V开发板上也能完成基本的流水灯实验。当然,大家也可以参照此方法,做更多的尝试,这样就不必受限于出厂的SOC核了
。现在将我的思路和工作步骤总结如下。
1. 因为我需要将E203 SOC的I/O引脚进行重新映射,这步工作是不牵扯GPIO接口部分的实现,更不牵扯RISC-V软核部分的实现,而它仅仅是E203 SOC引脚和板载物理设备的连接关系部分,所以从综合角度来说,它应该是最后被综合的那个文件。打开vivado2018.1可以看到综合顺序,如下图1中红色框1所示。
图1 综合顺序
从上图1中可以看到,红色框2以上就是E203软核和SOC对应的.v文件,而红色框2所对应的system.v文件就是上述曾分析的需要更改的文件,打开此文件后可以发现,它就是描述板载设备和E203 SOC接口的对应关系,当然这个和e203_0404.xdc文件还是有区别的(自行脑补吧
)。搞清楚这个了,那么接下来工作就简单了。
当然也可以通过另一个角度来查看,如下图2中红色框所示。点击vivado2018.1左边执行栏中的原理图选项,就可以在图2右侧看到所生成的e203 SOC原理图,经过放大查到led3引脚,点击右键选择Go to Source选项,也能找到system.v文件(当初我便是这么查找的该文件)。
图5 查看执行后生成的原理图
2. 因为E203 SOC总共对外提供有32个I/O引脚,如下图3中红色框和绿色字所示。这里,经过对system.v文件的分析以及我将要进行的工作,我计划把Pin9分配给了led_0,Pin23分配给了led_1,Pin18分配给了led_2,Pin14分配给了led_3。

图3 更改system.v文件中的引脚描述
接下来的工作,只需点击综合和执行即可,不出意外即可执行成功(这里所花时间和机器有关,我这里花了近5分钟才搞定)。紧接着工作就是generate bitstream,再根据perf-v实验教程进行flash固化即可。
write_cfgmem -format mcs -interface spix4 -size 128 -loadbit "up 0x0 D:/exp/Perf-V-IDE/50T/impl_1/system.bit" -force D:/exp/Perf-V-IDE/50T/impl_1/system.mcs
3. 搞定前述步骤后,打开Perf-V IDE新建一个流水灯的工程。这里需要修改一个hifive1.h头文件,如下图4所示。其文件路径如图4左边所示,在其中增加了对led1-3的宏定义。这里的值和上面修改的I/O引脚值是对应的。当然,如果发现板载流水灯的灯序不对,那么你只需在这里更改下led宏定义即可,和第2步所做的没有关系。

个人猜测Perf-V IDE的问题,这里想使用定时中断方式使led灯流水,没有搞定,所以这里采用的查询方式,代码如下所示。
#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include <string.h>
#include "plic/plic_driver.h"
#include "hifive1.h"
#include "encoding.h"
#include <unistd.h>
#include "stdatomic.h"
#define uchar unsigned char
#define uint unsigned int
uint32_t count=0;
uint32_t delay_mark=0;
void delay(uint32_t times);
void reset_demo (uint32_t times);
void GPIO_SET(uint32_t pin_num,uint32_t pin_val,uint32_t pin_model);
// Structures for registering different interrupt handlers
// for different parts of the application.
typedef void (*function_ptr_t) (void);
void no_interrupt_handler (void) {};
function_ptr_t g_ext_interrupt_handlers[PLIC_NUM_INTERRUPTS];
// Instance data for the PLIC.
plic_instance_t g_plic;
/*Entry Point for PLIC Interrupt Handler*/
void handle_m_ext_interrupt(){
plic_source int_num = PLIC_claim_interrupt(&g_plic);
if ((int_num >=1 ) && (int_num < PLIC_NUM_INTERRUPTS)) {
g_ext_interrupt_handlers[int_num]();
}
else {
exit(1 + (uintptr_t) int_num);
}
PLIC_complete_interrupt(&g_plic, int_num);
}
/*Entry Point for Machine Timer Interrupt Handler*/
void handle_m_time_interrupt(){
clear_csr(mie, MIP_MTIP);
volatile uint64_t * mtime = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
volatile uint64_t * mtimecmp = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
uint64_t now = *mtime;
uint64_t then = now +0.5* RTC_FREQ;
*mtimecmp = then;
count++;
set_csr(mie, MIP_MTIP);
}
void print_instructions() {
//write (STDOUT_FILENO, instructions_msg, strlen(instructions_msg));
//write (STDOUT_FILENO, instructions_msg_sirv, strlen(instructions_msg_sirv));
// printf ("%s",instructions_msg_sirv);
}
void reset_demo (uint32_t times){
// Disable the machine & timer interrupts until setup is done.
clear_csr(mie, MIP_MEIP);
clear_csr(mie, MIP_MTIP);
for (int ii = 0; ii < PLIC_NUM_INTERRUPTS; ii ++){
g_ext_interrupt_handlers[ii] = no_interrupt_handler;
}
// Set the machine timer to go off in 3 seconds.
volatile uint64_t * mtime = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
volatile uint64_t * mtimecmp = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
uint64_t now = *mtime;
uint64_t then = now + times*RTC_FREQ;
*mtimecmp = then;
// modified by Bob ma
// 2019-1-8
// Enable the Machine-External bit in MIE
//set_csr(mie, MIP_MEIP);
// Enable the Machine-Timer bit in MIE
//set_csr(mie, MIP_MTIP);
// Enable interrupts in general.
//set_csr(mstatus, MSTATUS_MIE);
}
void GPIO_SET(uint32_t pin_num,uint32_t pin_val,uint32_t pin_model)
{
if(pin_model==output)
{
GPIO_REG(GPIO_OUTPUT_EN) |= (0x01<<pin_num);
if(pin_val==1)
{
GPIO_REG(GPIO_OUTPUT_VAL) |=(0x01<<pin_num);
}
if(pin_val==0)
{
GPIO_REG(GPIO_OUTPUT_VAL) &=~(0x01<<pin_num);
}
}
if(pin_model==input)
{
GPIO_REG(GPIO_INPUT_EN) |= (0x01<<pin_num);
}
}
int main(int argc, char **argv)
{
int loop;
/**************************************************************************
* Set up the PLIC
*
*************************************************************************/
PLIC_init(&g_plic,
PLIC_CTRL_ADDR,
PLIC_NUM_INTERRUPTS,
PLIC_NUM_PRIORITIES);
reset_demo(1);
GPIO_SET(ledD4_r,1,output);
GPIO_SET(ledD4_g,1,output);
GPIO_SET(ledD4_b,1,output);
GPIO_SET(ledD5_r,1,output);
GPIO_SET(ledD5_g,1,output);
GPIO_SET(ledD5_b,1,output);
GPIO_SET(ledD6_r,1,output);
GPIO_SET(ledD6_g,1,output);
GPIO_SET(ledD6_b,1,output);
GPIO_SET(led4,1,output);
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
//GPIO_SET(BLUE_LED_OFFSET,1,output);
while (1){
if(count==1)
{
GPIO_SET(led1,0,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==2)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,0,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==3)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,0,output);
GPIO_SET(led4,1,output);
}
if(count==4)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,0,output);
}
if(count==5)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==6)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,0,output);
}
if(count==7)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,0,output);
GPIO_SET(led4,1,output);
}
if(count==8)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,0,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==9)
{
GPIO_SET(led1,0,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==10)
{
GPIO_SET(led1,1,output);
GPIO_SET(led2,1,output);
GPIO_SET(led3,1,output);
GPIO_SET(led4,1,output);
}
if(count==10)
{
count=0;
}
count++;
for(loop = 0; loop < 500000; loop++);
}
return 0;
}演示效果如下所示。
4. 写这些看似简单,但是摸索的过程真的令人很痛苦
。接下来的工作就是在读胡老师的姐妹书的基础上,依次搞定定时中断、按键中断、PWM以及UART吧,然后再逐步摸向E203软核内部。


