(多核DSP快速入门)8、利用MessageQ模块的多核灰度转换程序

原创文章

转载请注册来源 http://blog.csdn.net/tostq

       第3节我们介绍了一个单核的图像灰度转换程序,这一节我们将这个单核的图像灰度转换程序改成多核程序。

       这个多核程序的主要原理非常简单,将整张图片分块,每一块的处理对应一核,这里需要涉及的是IPC的三个模块:
       (1)SharedRegion模块:通过SharedRegion模块将输入图像缓存和输出图像缓存放到共享内存中,使其每个核都能操作图像数据
       (2)MessageQ模块:主核(核0)在将图像文件输入共享内存后,需要将输入图像缓存和输出图像缓存的地址打包成消息,传递给从核。
       (3)Notify模块:从核完成其对应图像块的灰度转换后,需要Notify模块通知主核,完成任务。

       本节Notify模块和SharedRegion模块在上一节已经讲过了,这里只是粗略说明其设置,本节将重点介绍MessageQ模块,同时一步一步完成这个多核灰度转换程序。

一、新建项目
       前面已经介绍如何新建CCS项目,这里就不再详细介绍了,不过比较推荐方式是先导入IPC模板例子,然后在模板上进入修改,这样减少了大量配置SYS/BIOS的时间。

二、导入SharedRegion模块
       导入SharedRegion模块的要点:
       (1)SharedRegion模块是由所有的核所共享,所以最好通过静态设置。在.cfg文件中设置SharedRegion的特性,下面是.cfg文件中的设置
       
       (2)主核0创建存放输入缓冲和输出缓冲的两个共享内存
       
       此时从核因处于等待消息,挂起状态。
       (3)从核收到消息,从消息中读取到两个共享内存的地址
       
       (4)地址转换。(前一节有提)
       (5)注意在往内存中写的时候,要通过Cache等函数将缓冲写入内存。
       在读的内存的时候,要失效Cache,防止读取到了Cache中的数据,而不是内存中数据。
       下面的这个场景,是核在处理完本身的任务后,等待其他核完成任务,读取共享内存前做的失效Cache的例子
       

二、导入MessageQ模块
       MessageQ模块,即消息队形(Message Queue)MessageQ同Notify模块一样,也是用于多核之间的通信的,不过不同的是,Notify模块更加侧重于通知,其只能传递一个参数,而MessageQ却可以传递变长度的消息,更侧重于传递消息,另外不同线程间的消息是独立的,例如对于每个MessageQ来说,存在一个读者却可能有多个写者。在本节的多核图像灰度转换例子来说,我们需要向从核传递两个32bits的地址(inBuf、outBuf),这通过Notify模块是无法办到(只能传递32bits以下的信息)。所以我们必须选择MessageQ模块。
       MessageQ模块的主要特点:
       1. 实现了处理期间变长消息的传递,所需要传递的消息一般超过32bit;
       2. 其消息的传递都是通过操作消息队列来实现的;
       3. 每个消息队列可以有多个写者,但只能有一个读者,而每个任务(task)可以对多个消息队列进行读写;
       4. 一个宿主在准备接收消息时,必须先创建消息队列,而在发送消息前,需要打开预定的接收消息队列;
Tips:
       1. 读者(Reader):读者(reader)是一个线程将消息队列中的消息读出,一个单一的读线程会调用MessageQ_create(),MessageQ_get(),MessageQ_free()及MessageQ_delete(),每一个读线程会拥有一个消息队列,而写线程会打开消息队列并把消息放入。
       2. 写者(Writer):写者(writer)则将消息写入消息队列中去,一个单一的写线程会调用MessageQ_open(),MessageQ_alloc(),MessageQ_put()及MessageQ_close()。


       MessageQ组件提供了以下几个API:
       消息队列初始化:MessageQ_Params_init()
       消息队列创建/销毁:MessageQ_create()/MessageQ_delete(),create创建消息队列,并分配相应存储空间
       消息队列打开/关闭:MessageQ_open()/MessageQ_close(),open时会返回远程处理器上的QueID的地址。
       为消息队列分配堆内存:MessageQ_alloc()/MessageQ_free()
       为消息队列注册/注销堆内存:MessageQ_registerHeap()/MessageQ_unregisterHeap()
       向消息队列中放入/获取消息:MessageQ_put()/MessageQ_get()
       其他逻辑API:
       获取消息队列ID:MessageQ_getQueueId()
       获取消息队列中消息数:MessageQ_count()
       在消息队列中嵌入消息:MessageQ_setReplyQueue()
       为消息队列解阻塞:MessageQ_unblock()
       为调试消息队列加入Trace:MessageQ_setMsgTrace()

(1)配置MessageQ模块,通过在.cfg文件中设置,具体的设置对象参数如下:
       MessageQ.maxNameLen :设置MessageQ名称的最大长度
       MessageQ.maxRuntimeEntries :设置可以创建的消息队形最大数目 
       MessageQ.numHeaps :系列中的Heap的数目
       MessageQ.tableSection:用于放置名称表的段名
       这里不需要上述设置,保持默认就可以了,只需要导入MessageQ模块及一个用于给消息分配堆空间的HeapBufMP模块(下一节将介绍)
       
Tips:核间连接需要设置启动MessageQ(默认不是启动的),
       
(2)创建MessageQ对象
       因为MessageQ对象并不是共享资源,每个消息队列只能有一个读者,所以消息对列MessageQ只能通过动态创建,而不支持静态创建,需要依赖于创建其的处理器核。其创建步骤仍然是先初始化参数后创建。这里需要给每个从核建立一个消息队列,消息队列接收由主核发送的消息。
       
       
       MessageQ_create创建函数第一个参数是消息队列名称,是string类型。
       
(3)消息空间初始化
       a). 构建消息结构,这里主要是创建可以打包SRPtr的消息结构体,消息的内部元素类型和数目可以任意,但任何消息结构定义都必须保证其第一个值为一个消息头结构,那么在MessageQ相关的函数的使用时,一般是用MessageQ_Msg来表示消息结构,所以需要将MyMsg强制转换成MessageQ_Msg。
       
       b). 给消息分配堆空间,所有发送给消息队列的消息都必须预先在Heap结构中分配,这里主要调用的HeapBufMP模块,这个模块主要分配定长的堆空间,在下节我们将具体介绍这个模块,这里先给出其用法,仍然是先初始化参数再创建,因为只需要发送一个消息就可以了,所以HeapBufMP就只分配一个消息块。另外我们也可以根据不同消息的用途来分配不同的堆,比如比较重要的消息可以分配到片上内存,而不怎么重要的消息就分配到外部内存的堆。
       
       c).  堆的注册与分配
       定义堆后,还需要将堆与消息队列挂钩,首先将堆定义一个ID号。MessageQ_registerHeap()函数给一个堆分配一个消息队列的堆编号,当分配一个消息时,将会利用这个堆编号,而不是堆句柄,这个堆编号应该是从0开始增长的,其最大数由MessageQ.numHeap来决定。
       
       之后,利用这个HeapId将堆空间分配给消息
       

       (4)发送一个消息
       a). 通过消息的名称来打开消息队列,并返回消息队列的ID,这个打开过程,需要等待循环
       b). 根据消息队列ID,将消息发送给消息队列
       c). 发送消息是在主核中完成,其需要给其他7个从核分别发送消息
       d). 注意的是要将自我定义的msg结构同MessageQ_Msg进行强制转换
       
      (5)打开一个消息
      创建MessageQ的从核通过MessageQ_get()函数来获得消息
      
      这里的handle表示消息队列的句柄,msg表示输出(打开)的消息,timeout表示等待打开消息的时间,MessageQ_FOREVER表示一直等待,直到收到消息。读取消息的内容:
      
      
      (6)消息及消息队列的删除
      这里非常需要注意的是,要处理各消息和各消息队列的释放顺序,比如当前释放消息队列时,也会把消息给释放了,而本节的例子只创建了一个消息堆由多个核共享,此时如果其他核正在处理消息,那么将会出现如下堆错误:
[TMS320C66x_1] ti.sdo.ipc.MessageQ: line 260: assertion failure: A_heapIdInvalid: heapId is invalid
[TMS320C66x_1] xdc.runtime.Error.raise: terminating execution
      a). 删除消息,释放消息堆空间. 消息的删除一般是由读线程完成的,当读者收到这个消息,其要么释放(删除)这个消息,要么重新利用这个消息。
      
      b). 消息队列的删除,消息的删除一般是由读线程完成的。
      
      c). 消息队列的关闭,当写线程(这里是主核)完成发送消息后并以后不再发送消息,其应该关闭消息队列
      
      (7)有关于MessageQ的其它内容
      MessageQ是一个复杂的消息传递模块,本节的例子只是提到了最为简单的用法,这里略微介绍下其它几个要点。
      a). 消息ID,MessageQ还可以为每个消息提供一个ID号,通过设置这个ID号,可以让读者识别消息的来源
      设置消息ID函数:MessageQ_setMsgId(msg, msgId)
      获取消息ID函数:MessageQ_getMsgId(msg, msgId)
      另外还有设置消息优先级函数:Void MessageQ_setMsgPri(MessageQ_Msg msg, MessageQ_Priority priority)
      b). 关于线程同步
      c). 回复队列(replyQueue),很多情况下不需要打开队列,就可以直接将对消息发送。有时很多消息发送后需要返回消息,就可以利用这个回复队列
消息的发送者,可以MessageQ_setReplyQueue通过给发送的消息设置一个回复队列ID号
      Void MessageQ_setReplyQueue(MessageQ_Handle handle, MessageQ_Msg msg);
      而消息的读者,可以通过MessageQ_getReplyQueue来获得回复队列ID号,然后通过MessageQ_put直接发送消息。
      MessageQ_getReplyQueue(msg)
      d). 远程通信,MessageQ也可用于多处理器之间远程通信。
      e). MessageQ的通信结构
      
三、导入Notify模块
      导入Notify模块主要是为了让从核通知主核,其任务完成。上一节已经介绍了Notify模块,这里就不多说,主要提下几个要点
      (1)首先要保证各核之间存在连接,因为8个核都要连接,所以可以直接设为
      
      (2)从核注册发送主核的事件
      
      (3)设置注册函数,这里需要设置一个信号量(信号量的设置第5节有),当完全收到七个从核的任务完成事件后,激活信号量.
      
      (4)从核给主核发送任务完成事件
      
      (5)主核一直等信号量激活
      

四、例程结果
      (1)关于图像灰度转换程序,这里为了方便,我们直接将图像以unsigned char的数组形式保存。
      这个程序非常简单,将图像分成8块,每个核负责其中一块的处理:
      
      (2)软件仿真读取图像的时间实在太长了,所以这里没有对图像进行处理了,只是处理一组数据
      读取数据
      
      读出数据
      
      (3)结果
      读取的数据结果:
      
      前面两个255 255是主核的处理任务,这里主核没有完成处理,所以我们需要注意是要将主核同从核的任务分开,让从核只做计算任务,而主核负责全局的数据处理。
      地址结果:
      
  • 11
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 37
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值