FPGA开发技能(6)Qt生成ROM IP核使用的COE文件

在这里插入图片描述

前言

在vivado中的一些些IP核的配置中,需要使用COE(Coefficient)文件来传递参数,例如FIR滤波器所需的滤波系数文件以及RAM中的初始化数据文件等。COE文件是一种ASCII文本文件,文件头部定义数据基数(Radix),可以时2、10或16。数据以向量的形式给出,向量以分号结尾,向量之间用逗号隔开。Vivado会解析COE文件格式,并在生成IP核时导出相关的MIF格式文件。

1.简介

本次小项目生成COE文件是为了配置Block Memory Generator IP核时使用,将数据写入COE文件,传递给IP核,在vivado的应用中使用。如何写coe文件与vivado具体的应用息息相关,本次应用是将png、jpg等常见的图片灰度化、二值化后,写入coe中,位宽是128bit。coe中以32个16进制数据为一组,每个16进制是4bit,32个即为128bit。
Matlab作为主流的算法平台与验证平台之一往往成为很多数据的重要来源,因此在matlab下生成coe文件是非常普遍的操作,例如使用matlab提供的一些文件操作函数如fprint和fclose。除此之外,python提供了扩展库Numpy,可以方便地对矩阵操作,利用python生成coe也是比较简便的。本文使用qt写coe文件,也并不复杂。

2.coe文件格式

memory_initialization_radix = 10;
memory_initialization_vector = 1,2,3,4,5,6,…,99;
其中:
memory_initialization_radix 是数值格式
memory_initialization_vector 是初始化的数值向量,分别对应各个深度
结合我们的应用需求,数值格式为16,数值向量是32个16进制数为一组。

3.开发步骤

1.搭建界面,简单布局
在这里插入图片描述
选择图片弹出文件对话框,选择将要生成coe的图片;QtextEdit用来打印当前状态,与用户做一些简单交互;clearBox用来清除状态提示框。
2.初始化Init()
初始化成员变量,打印当前状态,连接按钮信号和对应的槽。由于我的应用中只对应三种分辨率的图片,分别是“25601600”、“20481080”、“1024*768”。因此用户输入其他分辨率的图片将会提示输入不正确,程序不做任何处理。
3.槽函数
clearBox按钮的槽函数直接将edit清除就可以。选择图片的按钮则是要,打开文件选择框,用户选择标准图片后,对图片进行灰度化,二值化,然后移位处理,最后写成coe文件。
具体实现步骤如下:

  • 首先,通过用户点击路径“获取”图片对象
    QString fileName = QFileDialog::getOpenFileName(this,"Seclect img","F:\\");
    if(fileName.isEmpty())
    {
        return;
    }
    QFileInfo fileInfo=QFileInfo(fileName);//获取文件信息
    QImage img = QImage(fileName);
  • 然后,使用QImage方法,对其进行灰度化
img=img.convertToFormat(QImage::Format_Grayscale8);//将png、jpg转换成8位灰度 这个算法是Qt内部实现的
  • 然后,保存灰度化数据
savePix = img.bits(); //保存灰度化数据
  • 然后计算二值化阈值,采用求平均的方法(结合代码查看)
int  threshold_value=average(savePix,height*width);
  • 然后,根据灰度化数据计算并保存二值化数据
 for(int j=0;j<width*height;j++)//计算保存二值化数据
    {
        
        if(savePix[j]>threshold_value)
        {tempbin[j]=0;}
        else
        {tempbin[j]=1;}
    }
  • 然后,将二值化数据移位处理成1个16进制数的4bit分别代表4个像素数据
for (int i = 0;i<width*height/4;i++ )
        for(int j = 0;j<4;j++)
        {
            if(j==0)
            {
                temphex = tempbin[i*4];
            }
            else
            {
                temphex=(temphex<<1)+tempbin[i*4+j];
            }
        }
        dataBuf[i]=temphex;
  • 最后,调用写coe文件函数,将dataBuf中的数据写到coe中
bool Widget::writeCoe7000(QFileInfo info)
{
     QString str=info.baseName();
     str.append(".coe");
     FILE *fp= fopen(str.toLatin1(),"w");
     fprintf(fp, "memory_initialization_radix = 16;\n");
     fprintf(fp, "memory_initialization_vector = \n");
     for(int q=0;q<6144;q++)
     {
         for (int i =0;i<32 ;i++ )
         {
             {fprintf(fp, "%x", dataBuf[q*32+i]);}
         }
        fprintf(fp, ",\n");//每128bit,即每32个16进制数据写一个逗号加换行
     }
     fclose(fp);
     return true;
}

4.效果

生成的coe与程序截图
在这里插入图片描述

5.代码

main

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    Init();
}
Widget::~Widget()
{
    delete ui;
}
void Widget::Init()
{
    //init 成员变量
    height = 0;
    width = 0;
    temphex = 0;
    currentState("请选择标准图片2560*1600或2048*1080或1024*768"); //调用显示当前状态函数
    currentState("请注意在使用时将最后一个逗号改为分号才能进入vivado使用"); //调用显示当前状态函数
    //关联按钮信号与槽
    connect(ui->clearBtn,SIGNAL(clicked(bool)),ui->textEdit,SLOT(clear()));
    connect(ui->selBtn,SIGNAL(clicked(bool)),this,SLOT(doProcessSelBtn(bool)));
}
void Widget::currentState(QString str)
{
    dataTime = QDateTime::currentDateTime();
    dataStr= dataTime.toString( "yyyy年MM月dd日 hh:mm:ss");
    dataStr.append(":   ");
    dataStr.append(str);
    ui->textEdit->append(dataStr); //设置显示当前系统时间
    dataStr.clear();
}
int Widget::average(uchar *ary, int len)
{
     int sum=0;
     for (int i=0;i<len;i++)
     {
         sum+=ary[i];
     }
     return sum/len;
}
void Widget::doProcessSelBtn(bool)
{
    QString fileName = QFileDialog::getOpenFileName(this,"Seclect img","F:\\");
    if(fileName.isEmpty())
    {
        return;
    }
    QFileInfo fileInfo=QFileInfo(fileName);//获取文件信息
    QImage img = QImage(fileName);
    width = img.width();
    height = img.height();
    qDebug()<<width<<height;
    if((width!=2560)||(height!=1600))//不是DLP9000X
    {
        if((width!=2048)||(height!=1080))//不是DLP9500
        {
            if((width!=1024)||(height!=768))//不是DLP7000
            {
                currentState("此次输入无效,请选择标准图片输入");
                return;
            }
        }
    }
    //判断过程结束,是标准图片
    img=img.convertToFormat(QImage::Format_Grayscale8);//将png、jpg转换成8位灰度 这个算法是Qt内部实现的
    savePix = img.bits(); //保存灰度化数据
    tempbin = new int[height*width];//二值化数据存储空间
    int  threshold_value=average(savePix,height*width);//二值化算法(求平均)
    for(int j=0;j<width*height;j++)//计算保存二值化数据
    {
        //这里01赋值已经验证过
        if(savePix[j]>threshold_value)
        {tempbin[j]=0;}
        else
        {tempbin[j]=1;}
    }
    dataBuf = new char[width*height/4];//使用一个数组元素保存四个像素数据。(一个16进制为4位二进制)
    for (int i = 0;i<width*height/4;i++ )//1024000=2560*1600/4,相当于每一个databuf中保存
    {
        for(int j = 0;j<4;j++)
        {
            if(j==0)
            {
                temphex = tempbin[i*4];
            }
            else
            {
                temphex=(temphex<<1)+tempbin[i*4+j];//已经验证!!!
            }
        }
        dataBuf[i]=temphex;
    }
    if(width==2560)//9000X
    {
       if(writeCoe9000x(fileInfo))
       {
           currentState("DLP9000X---COE文件已生成,请在软件目录下查看!");
       }
       else
       {
           currentState("DLP9000X---COE文件生成失败,请重新输入!");
       }
    }
    if(width==2048)//9500
    {
        if(writeCoe9500(fileInfo))
        {
            currentState("DLP9500---COE文件已生成,请在软件目录下查看!");
        }
        else
        {
            currentState("DLP9500---COE文件生成失败,请重新输入!");
        }
    }
    if(width==1024)//7000
    {
        if(writeCoe7000(fileInfo))
        {
            currentState("DLP7000---COE文件已生成,请在软件目录下查看!");
        }
        else
        {
            currentState("DLP7000---COE文件生成失败,请重新输入!");
        }
    }
    delete [] tempbin;
    delete [] dataBuf;
}
bool Widget::writeCoe7000(QFileInfo info)
{
     QString str=info.baseName();
     str.append(".coe");
     FILE *fp= fopen(str.toLatin1(),"w");
     fprintf(fp, "memory_initialization_radix = 16;\n");
     fprintf(fp, "memory_initialization_vector = \n");
     /*说明:
     coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
     对于7000来说,一行1024个数据,只有一组数据通道,256个16进制数据为一行。
     一帧1024*768的图片总共有1024*768/4个数据保存,而1024*768/4 = 32*6144(32代表一行coe数据,6144代表一共这么多行)
     另外每一行,每128bit,每32个16进制数都要写一个逗号。
     */
     for(int q=0;q<6144;q++)
     {
         for (int i =0;i<32 ;i++ )
         {
             {fprintf(fp, "%x", dataBuf[q*32+i]);}
         }
        fprintf(fp, ",\n");//每128bit,即每32个16进制数据写一个逗号加换行
     }
     fclose(fp);
     return true;
}
bool Widget::writeCoe9500(QFileInfo info)
{
    QString str=info.baseName();
    QString str2=info.baseName();
    str.append("_ab.coe");
    str2.append("_cd.coe");
    FILE *fp= fopen(str.toLatin1(),"w");
    FILE *fp2= fopen(str2.toLatin1(),"w");
    fprintf(fp, "memory_initialization_radix = 16;\n");
    fprintf(fp, "memory_initialization_vector = \n");
    fprintf(fp2, "memory_initialization_radix = 16;\n");
    fprintf(fp2, "memory_initialization_vector = \n");
    /*说明:
    coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
    对于9500来说,一行2048个数据,也就是有512个16进制数保存一行数据。那么前256个数据属于是ab通道,后256个数据是cd通道。
    一帧2048*1080的图片总共有2048*1080/4个数据保存,而2048*1080/4 = 32*17280(32代表一行coe数据,17280代表一共这么多行)
    另外每一行,每128bit,每32个16进制数都要写一个逗号。
    */
    for(int q=0;q<17280;q++)
    {
        for (int i =0;i<32 ;i++ )
        {
            if((q*32+i)%512<256)//前320个属于ab通道
            {fprintf(fp, "%x", dataBuf[q*32+i]);}
            else
            {fprintf(fp2, "%x", dataBuf[q*32+i]);}
        }
           if((q*32+31)%512<256)//每128bit,即每32个16进制数据写一个逗号
           {fprintf(fp, ",\n");}
           else
           {fprintf(fp2, ",\n");}
    }
    fclose(fp);
    fclose(fp2);
    return true;
}

bool Widget::writeCoe9000x(QFileInfo info)
{
    QString str=info.baseName();
    QString str2=info.baseName();
    str.append("_ab.coe");
    str2.append("_cd.coe");
    FILE *fp= fopen(str.toLatin1(),"w");
    FILE *fp2= fopen(str2.toLatin1(),"w");
    fprintf(fp, "memory_initialization_radix = 16;\n");
    fprintf(fp, "memory_initialization_vector = \n");
    fprintf(fp2, "memory_initialization_radix = 16;\n");
    fprintf(fp2, "memory_initialization_vector = \n");
    /*说明:
    coe文件一个数据的位宽是128bit,也就是需要32个16进制数,即32*4bit = 128。
    对于9000X来说,一行2560个数据,也就是有640个16进制数保存一行数据。那么前320个数据属于是ab通道,后320个数据是cd通道。
    一帧2560*1600的图片总共有2560*1600/4个数据保存,而2560*1600/4 = 32*32000(32代表一行coe数据,32000代表一共这么多行)
    另外每一行,每128bit,每32个16进制数都要写一个逗号。
    */
    for(int q=0;q<32000;q++)
    {
        for (int i =0;i<32 ;i++ )
        {
            if((q*32+i)%640<320)//前320个属于ab通道
            {fprintf(fp, "%x", dataBuf[q*32+i]);}
            else
            {fprintf(fp2, "%x", dataBuf[q*32+i]);}
        }
           if((q*32+31)%640<320)//每128bit,即每32个16进制数据写一个逗号
           {fprintf(fp, ",\n");}
           else
            {fprintf(fp2, ",\n");}
    }
    fclose(fp);
    fclose(fp2);
    return true;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDateTime>
#include <QFileInfo>
#include <QFileDialog>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void Init();//初始化成员变量
    void currentState(QString str);//显示当前时间戳加状态信息
    int average(uchar *ary, int len);//计算二值化阈值
private slots:
    void doProcessSelBtn(bool);
    bool writeCoe7000(QFileInfo info);
    bool writeCoe9500(QFileInfo info);
    bool writeCoe9000x(QFileInfo info);
private:
    Ui::Widget *ui;
    QDateTime dataTime;//当前时间
    QString dataStr;//text字符串
    int height;//图片高
    int width;//图片宽
    int *tempbin;//保存一帧图片的二值化数据
    uchar *savePix;//保存一帧图片的灰度化数据(8bit)
    char *dataBuf ;//一个数组元素对应四个像素的二值化数据,即使用一个16进制代表四位二进制。
    uchar temphex;
};
#endif // WIDGET_H

6.传送门

END

💎文章原创,首发于CSDN论坛。
💎欢迎点赞💖收藏✨打赏💷!
💎欢迎评论区🎤或私信指出错误🎤,🗣️提出宝贵意见或疑问。


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑾芳玉洁错过的烟火

原创不易,请多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值