Perf-V IDE下的流水灯实验

mabo124 2019-1-10 4667

       非常感谢有机会接触澎峰科技出品的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。

                                                                                  图2     I/O引脚分配
          有了以上结论,在vivado2018.1中更改system.v文件即可,如下图3所示。当然这里,你也可以使用其他引脚,只需更改或者注释掉相关语句即可。我这里本想用Pin15,但是它预留给了btn_0了,只好作罢;而本人又不清楚QSPI1_SS2有何作用,故而拿来作为led_0用了。

                                                           图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步所做的没有关系。

图4   修改hifive1.h文件

          个人猜测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软核内部。

最新回复 (1)
返回
发新帖
作者最近主题: