cuter

Vivado HLS何去何从?——近日的一些尝试

0
阅读(13093) 评论(11)

Vivado HLS何去何从?

          ——近日的一些尝试

如何更好的利用vivado hls是我近期一直在思考的问题。是不是一般的c程序都能很好的转换为IP核?应用的限制在哪里?hls更适合用来做什么?


带着这些疑问,做了以下一些尝试:

1、尝试使用hls实现zedboard oled的驱动

看过我之前博客的朋友会知道,我在zedboard oled上花过一些功夫。值得高兴的是,我之前的方案已经被不少网友用到自己的设计当中。虽然如此,我一直觉得用纯软件的方式来驱动oled效率太低。有网友提出以PL为核心的驱动方式,由于这种方案需要将显示内容预存储在PL的ROM中,所以很不灵活。比较好的方案还是要ps+pl配合实现的,用双口ram或者ddr做oled的显存,ps负责显示内容控制,pl负责oled的刷新、显示。虽然一直心存改进的念头,但是由于种种原因,一直没有动手。

这次借着hls的东风,想尝试把之前的驱动程序稍作修改,修改成hls能够直接综合的代码,然后做成IP,从而使读写时序均由pl完成。目前为止,还没有令人振奋的成果。

这里把所做的设计放出来,希望感兴趣的网友能够一起研究讨论,能获得官方高手的指点就更好了。

首先是代码对比:

arm c代码为:


//更新显存到LCD
void OLED_Refresh_Gram(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
       write_cmd (0xb0+i);    //设置页地址(0~7)
       write_cmd (0x02);      //设置显示位置—列低地址,偏移了2列
       write_cmd (0x10);      //设置显示位置—列高地址
       for(n=0;n<128;n++)write_data(OLED_GRAM[n][i]);
    }
}
//向SSD1306写入一个字节的命令。
void write_cmd(u8 data)
{
    u8 i;
    Clr_OLED_DC;
    for(i=0;i<8;i++)
    {
       Clr_OLED_SCLK;
       if(data&0x80)
           Set_OLED_SDIN;
       else
           Clr_OLED_SDIN;
       Set_OLED_SCLK;
       data<<=1;
    }
}
//向SSD1306写入一个字节的数据。
void write_data(u8 data)
{
    u8 i;
    Set_OLED_DC;
    for(i=0;i<8;i++)
    {
       Clr_OLED_SCLK;
       if(data&0x80)
           Set_OLED_SDIN;
       else
           Clr_OLED_SDIN;
       Set_OLED_SCLK;
       data<<=1;
    }
}


hls c代码为:


void oled_refresh_gram(char *dc, char *sclk, char *sdin)
{
    char i,n;
    for(i=0;i<8;i++)
    {
       write_cmd (0xb0+i,dc, sclk, sdin);
       write_cmd (0x0,dc, sclk, sdin);
       write_cmd (0x10,dc, sclk, sdin);
       for(n=0;n<128;n++)write_data(oled_gram[n+128*i],dc, sclk, sdin);
    }
}
// cmd write function
void write_cmd(unsigned char data, char *dc, char *sclk, char *sdin)
{
    char i;
    *dc = 0;
    for(i=0;i<8;i++)
    {
       *sclk = 0;
       if(data&0x80)
       {
           *sdin = 1; //Set_OLED_SDIN;
       }
       else
       {
           *sdin = 0; //Clr_OLED_SDIN;
       }
       *sclk = 1;    //Set_OLED_SCLK;
       data<<=1;
    }
}
void write_data(unsigned char data,char* dc,char* sclk,char* sdin)
{
    char i;
    *dc = 1;
    for(i=0;i<8;i++)
    {
       *sclk = 0;
       if(data&0x80)
       {
           *sdin = 1;
       }
       else
       {
           *sdin = 0;
       }
       *sclk = 1;
       data<<=1;
    }
}


然后是仿真结果:

不知道电脑抽什么风,一到仿真就卡住,这里就没图了,总之结果是不对的,之前仿真过一次,结果是sclk、sdin、dc三根线状态都不正常,有高阻态出现……


2、尝试使用hls将axis-stream相关库函数

产生这个念头,是看到某个文档,好像是ug902提到stream库函数的使用。

主要尝试了2方面,另有一个小的想法。

i)                axis2vesa转换
尝试将axis-stream数据流按照标准vesa时序送显示器。没有成功,主要原因在于在库函数提供的数据结构中,有些控制信号是不可见的,例如valid和tready信号,无法利用这些信号控制什么时候开始读写数据。从hls生成的IP接口来看,valid、tready都是有的,所以猜测,这些信号是由hls自动管理的。

虽然说遇到一些困难,但从过程来看,利用hls实现协议转换虽然有一点难度,但是是可以实现的。OpenCV库函数mat2axivideo、axivideo2mat也是一种转换,和这里的尝试本质上上一样的。

ii)            仅处理数据,如画面叠加的实现。
在尝试axis2vesa设计的时候,发现想要“控制”是很难的,如果只处理数据,难度倒不是很大,所以考虑只处理数据。例如,两幅画面的数据流叠加后输出,这点是很容易实现的。我之前的博客里提到的“边标识填充算法”也可以利用HLS实现,可以降低程序设计的难度。这里也算是对axis数据流的处理。

另外还有一个想法,是否能够将linebuffer作oled的显示帧存?这个还没有尝试。


3、利用HLS实现信号发生

这个是相当easy的,传统FPGA在设计正弦波、三角波、方波等常规波形信号发生器时,往往采用ROM预存储波形+周期读取的方法实现,HLS的帮助下,波形发生器的实现起来就非常简单。只需要用C语言生成波形即可,剩下的工作交于HLS完成。从正弦波的例子可以看出,一些数学函数波形的生成都是类似的。

举例而言:

正弦波HLS 关键c代码:

void init_sin_table(din1_t sin_table[256])
{
   int i;
   for (i = 0; i < 256; i++) {
      dint_t real_val = sin( 2 * M_PI * (dint_t)i / 256.0);
      sin_table[i] = (din1_t)(512.0 + 512.0 * real_val);
   }
}


地址线宽度:8bits,数据线宽度:10bits,M_PI=3.14159265358

仿真结果:

三角波仿真结果(代码就不给了,比正弦波还简单):


有这些想法,总的来说是想利用hls进行偷懒,倒不是说这些想法有多大的实用价值,主要目的是想搞明白到底该如何去用hls这个东西。很多时候,我们拿着官方提供的design example,在自己电脑上做一遍,觉得很简单,然而这还远远不够。重要的是,如何把hls结合到自己的实际需求中去,我手里有一个需求,用hdl做起来麻烦,hls能不能帮助我降低设计难度?我该怎么着手设计我自己的程序?

从官方给的opencv相关应用,以及文中所做的尝试来看,hls的强大应该是在数据的处理上面,比如本文提到的图像叠加、波形发生器的实现,之前文章里提到的rgb2yuv的实现,在本质上都是对数据进行处理然后送出。如果想用hls做控制,可能就需要下一番功夫了。比如上文提到的oled驱动开发,如果使用hdl进行设计或许比用hls更快一些。


版权声明:

本文由博主“cuter”发布。欢迎转载,但不得擅自更改博文内容,也不得用于任何盈利目的。转载时不得删除作者简介和版权声明。如有盗用而不说明出处引起的版权纠纷,由盗用者自负。

博客官方地址:

ChinaAET:http://blog.chinaaet.com/cuter521

EDN China: http://bbs.ednchina.com/BLOG_cuter521_356737.HTM

  1. 厉害了我的哥

  2. 最近也早做驱动,感觉有点无从下手

  3. 换了工作,,真的假的??

  4. @crazylisely   

    Hi, cuter, 你这里的正弦波部分有一行代码我有些疑问:

          sin_table[i] = (din1_t)(512.0 + 512.0 * real_val);

    这里的 512 + 512* real_val 是对计算在(-1,1)间的浮点值进行定点化吧。输出宽度10bit,1bit符号位,9bits的数据位 但是我觉得这个语句好像不对吧?

    我的想法是:

      if (real_val >0)

       {    

    sin_table[i] = (din1_t) (512.0*real_val);

       }

    else

      

    sin_table[i] = (din1_t) (1024-512.0*real_val);

    另外,按照你这里的C代码描述的定点化 我在matlab进行了下验证:

    n= 0: 255;    % 时间序列

    a = sin(2 *pi*n/ 256) ;  % 正弦离散序列

    b = 512 + 512*a;   % 参照代码进行的定点化, 10 bits [1,9]

     z= find(b>=512);  % 查找负数

    c= b;      

    c(z)= -(1024-c(z));  % 负数的补码表示

     figure(1)  % 绘图

    plot(n,c)


    和你的想法没有区别啊,我只是给Y轴加了偏移量而已,使整个波形上移512,至X轴之上

  5. @crazylisely   


    据了解,这位大牛最近换了个工作,好像不搞这块了

  6. @crazylisely   

    Hi, cuter, 你这里的正弦波部分有一行代码我有些疑问:

          sin_table[i] = (din1_t)(512.0 + 512.0 * real_val);

    这里的 512 + 512* real_val 是对计算在(-1,1)间的浮点值进行定点化吧。输出宽度10bit,1bit符号位,9bits的数据位 但是我觉得这个语句好像不对吧?

    我的想法是:

      if (real_val >0)

       {    

    sin_table[i] = (din1_t) (512.0*real_val);

       }

    else

      

    sin_table[i] = (din1_t) (1024+512.0*real_val);

    另外,按照你这里的C代码描述的定点化 我在matlab进行了下验证:

    n= 0: 255;    % 时间序列

    a = sin(2 *pi*n/ 256) ;  % 正弦离散序列

    b = 512 + 512*a;   % 参照代码进行的定点化, 10 bits [1,9]

     z= find(b>=512);  % 查找负数

    c= b;      

    c(z)= -(1024-c(z));  % 负数的补码表示

     figure(1)  % 绘图

    plot(n,c)



  7. Hi, cuter, 你这里的正弦波部分有一行代码我有些疑问:

          sin_table[i] = (din1_t)(512.0 + 512.0 * real_val);

    这里的 512 + 512* real_val 是对计算在(-1,1)间的浮点值进行定点化吧。输出宽度10bit,1bit符号位,9bits的数据位 但是我觉得这个语句好像不对吧?

    我的想法是:

      if (real_val >0)

       {    

    sin_table[i] = (din1_t) (512.0*real_val);

       }

    else

      

    sin_table[i] = (din1_t) (1024-512.0*real_val);

    另外,按照你这里的C代码描述的定点化 我在matlab进行了下验证:

    n= 0: 255;    % 时间序列

    a = sin(2 *pi*n/ 256) ;  % 正弦离散序列

    b = 512 + 512*a;   % 参照代码进行的定点化, 10 bits [1,9]

     z= find(b>=512);  % 查找负数

    c= b;      

    c(z)= -(1024-c(z));  % 负数的补码表示

     figure(1)  % 绘图

    plot(n,c)


  8. @cuter
    我也想业余时间搞一搞,大神求带哈哈
  9. @木易

    业余时间应该会继续研究这些东西
  10. 亲,去新东家还接着玩Vivado吧!

  11. 沙发~~哈哈~~