DSP/BIOS簡介

原文:http://blog.sina.com.cn/s/blog_8d4b53e50100vjye.html


1 實時操作系統
  簡單地說,實時操作系統與一般意義上的操作系統(如Windows、  Unix等)的主要差別就在於實時操作系統提供了一種機制,使得運行於其上的應用程序都能夠滿足實時性的要求。  在Windows中常見的沙漏現象(即用戶等待現象)在實時系統中是絕對不允許的。因爲這可能造成通訊中斷,馬達損毀等災難性的結果。  DSP/BIOS是特別針對德州儀器C5000,C6000,C28XX系列的運行於DSP之上的一個實時操作系統。  DSP/BIOS實際上是一個可調用的系統模塊API的集合。以下就各個模塊分別加以介紹。


2 LOG
  在C語言開發的時候通常需要使用printf()來顯示當前狀態。但是printf()是非常花費時間的函數、而且不具有實時性。因爲DSP需要對顯示的數據進行分析,整理成合適的顯示格式,並調用輸出顯示模塊。所以在一個實時性要求很高的應用中,對printf()的調用可能會使系統根本無法滿足實時要求。在DSP/BIOS中引入了一個相應的函數LOG_printf()。該函數是LOG對象的一個方法(或調用函數〕。LOG對象本質上是一個32bit的整形數,其高低16bit分別代表要顯示的兩個數據。例1是分別使用printf()和LOG_printf()作比較的示範程序:
  #include  <stdio.h>  
  
  #include  <std.h>
  #include  <log.h>

  
  void  func_printf();
  void  func_LOG_printf();
  
  
  extern  LOG_Obj  logTrace;
  
  void  main  ()
  {
  return;
  }

  void  func_printf(int  time)
  {
  printf(″Strart  printf  demon″); 
  printf(″Current  time=%d  n″  、  time);
  printf(″End  printf  demon″);
  return;
  }

  void  func_LOG_printf(int  time)
  {
  LOG_printf(&logTrace、″Strart  LOG_printf  demon″);
  LOG_printf(&logTrace、    ″Current  time=%d  n″  、  time);
  LOG_printf(&logTrace、  ″End  LOG_printf  demon″);
  return;
  }
  func_printf()和func_LOG_printf()由DSP內時鐘控制每100ms週期性地分別調用一次。通過對printf()和LOG_printf()運行時間作比較發現,在C6211運行在150MHz的情況下,printf()需花費4000個週期約26.7μs,LOG_printf()只花費36個週期約0.24μs。printf()比LOG_printf()多開銷100倍以上的時間,因此LOG_printf()對於實時地顯示一些運行狀態是非常有幫助的。而且對於熟悉C語言的開發者來說,LOG_printf()的調用格式幾乎與printf()完全一樣。

 

3  STS
  對一個軟件進行分析優化時,通常會用到profile的功能。但是在實時運行的DSP的環境中使用profile等效於加入了多個程序斷點。  由於現在的DSP通常具有很深的流水線結構來保證DSP的高運算能力,如德州儀器的C6000系列的流水線長度爲12級,程序斷點需要排空所有已經進入流水線的指令。這樣也就破壞了真正的運行環境。同時profile還必須調用輸出模塊向主機傳遞時間信息。因此在profile的情況下真正的實時運行環境是沒有辦法得到保護的。DSP/BIOS針對這種情況引入了一個統計模塊STS。STS對象只有4個數據Previous、Count、Total和Max。調用的方法(API)也只有4個,即STS_add()、  STS_set()、  STS_delta()和STS_reset()。如果要對某一段程序進行分析時,只需在其前後調用STS_set和STS_delta就可以了。如例2使用STS測試程序段執行週期如下:
  
  #include  <sts.h>
  #include  <clk.h>

  
  void  func_load();

  
  extern  STS_Obj  stsLoad;

  
  void  main()
  {

  
   return;
  }

  void  func_load()
  {
  STS_set(&stsPrintf、  CLK_gethtime());

  
  ...
  STS_delta(&stsPrintf、  CLK_gethtime());
  }
  func_load()爲一箇中斷服務程序(ISR)。在C6211,150MHz的情況下,僅插入33個週期,約0.22μs。


4  任務調度(HWI/SWI/TSK)
  一個操作系統的核心永遠都是任務的調度。在DSP/BIOS中任務的調度是通過HWI、SWI和  TSK  三個模塊來實現的。這三個模塊分別對應於不同的調度方法。HWI即硬件中斷。在  DSP/BIOS中硬件中斷主要負責從外部設備中讀寫數據。由於硬件中斷直接與硬件打交道,所以對應的中斷服務程序ISR應該儘可能地短小精悍。需要注意的是HWI並不引起任務調度,因此在ISR的入口和出口成對地調用_HWI_enter()和_HWI_exit()這兩個宏是必須的。HWI在處理完數據的輸入輸出後調用SWI_post()來調度相應的軟件中斷,SWI來完成數據處理工作。SWI是  DSP/BIOS任務調度的核心,共有14個優先級,每個優先級可以有多個任務。SWI任務是搶斷式的,即高優先級的任務可以搶斷低優先級的任務。但是SWI任務是不可阻塞的。所有SWI任務共享一個堆棧,SWI任務只能在程序編制時預先定義好。DSP/BIOS中對任務的動態產生和對阻塞狀態的支持是通過TSK模塊來實現的。TSK有15個優先級,也是可以搶斷的,但是每個TSK任務使用獨立的堆棧。TSK任務是通過TSK_create()和TSK_delete()來動態生成和結束的。

 

5  同步(SEM/ATM/QUE/MBX)
  多任務系統中多個任務之間的協調同步工作可以通過多種方法來實現。常用方法如信號量、原子量、隊列和郵箱等。在DSP/BIOS中對這些方法的支持分別通過模塊SEM、ATM、QUE和MBX來實現。由於這些方法的使用與一般的操作系統完全一樣,在這裏就不再贅述了。僅就最靈活的在SWI中使用Mailbox的方法來加以簡單地說明。每個SWI任務都帶有一個Mailbox,對它的操作可以是計數型的SWI_inc()、  SWI_dec()  也可以是比特位操作型的  SWI_or()、  SWI_andn()。Mailbox控制SWI任務被調度的條件。

    or操作是將Mailbox中的某一位置1,同時引起SWI任務的調度。當一個SWI任務可能由多個事件觸發時,使用or操作可以方便地表示出觸發的事件。

    andn操作是將Mailbox中的某一位清0,如果Mailbox爲0,則引起SWI任務的調度。一個SWI任務需要多個條件都滿足時才運行的情況下,使用andn操作可以方便地表示出這些條件的狀態。

    inc和dec操作則更加靈活,用戶可以藉此實現多種應用。唯一需要注意的是,inc操作總是引起任務調度,而dec操作僅在Mailbox減到0時才引起一次任務調度。

 

6  通訊(PIP/SIO/HST)
一個系統如何從外部設備中取得數據,向外部設備輸出數據,如何在兩個任務之間進行數據正常交換是多樣靈活的。但是這種多樣性也給軟件的維護升級以及模塊化工作帶來許多不利因素。因此在保持多樣性的同時,保持接口的一致性對於一個軟件來說是非常有幫助的。考慮到DSP大多數是通過某種類型的串行接口如中繼線E1、IIS、SPI、同步串行口等與外部設備進行數據交換的,所以在DSP/BIOS中提供了兩種非常有用的接口對象PIP和  SIO。
  PIP對象包含一個緩衝隊列,與之對應的有兩個任務讀和寫。例3,例4分別是一個PIP對象對應的讀任務和寫任務的示範程序。

    例3  PIP對應的讀任務:
  extern  far  PIP_Obj  pip;
  reader()
  {
  Uns  size;
  Ptr  addr;
  if(PIP_getReaderNumFrames(&pip)>0)
  {
  PIP_get(&pip);
  addr=PIP_getReaderAddr(&pip);
  size=PIP_getReaderSize(&pip);

  
  PIP_free(&pip);
  }
  else{
  LOG_error(″no  frames  available″);
  
  }
  }
  例4  PIP對應的寫任務:
  extern  far  PIP_Obj  pip;
  writer()
  {
  Uns  size;
  Ptr  addr;
  if(PIP_getWriterNumFrames(&pip)>0){
   PIP_alloc(&pip);
   addr=PIP_getWriterAddr(&pip);
   size=PIP_getWriterSize(&pip);
   
   PIP_put(&pip);
  }
  else{
   LOG_error(″no  frames  available″);
   
  }
  }
  由邏輯關係可以看到,通過使用PIP應用程序可以保持一個簡單統一接口而不必關心具體的硬件操作,因此當該軟件移植到不同環境中時,至多只需要改寫設備驅動程序。使用PIP的一個具體實例就是HST模塊。HST模塊在主機和DSP之間建立起一條數據鏈路,該鏈路就是一個PIP對象。對HST的操作方式與PIP一致。其差別僅僅在於HST在初始化時指向了預定義的DSP上的HPI接口而已。
  SIO:從PIP的邏輯關係可以看出,讀寫PIP就是一個數據拷貝的過程。這在某些應用中,如實現網絡協議TCP/IP時,不是非常有效。因爲數據每向上傳遞一層就需要進行一次數據拷貝,其效率非常差。如果採用SIO來實現就會有很大的改善。SIO的操作只有get()和  put()兩種。與PIP不同的是SIO沒有自己的緩衝隊列。每次get()  或  put()  操作時都會在應用程序和設備驅動程序之間交換緩衝的指針。所以SIO操作的實質是數據地址的交換。由於沒有數據拷貝,其運行效率就很高。

 

7 RTDX
  實時數據交換Real-Time-Data-eXhange是DSP/BIOS提供的一個全新的功能。在很多應用中要求DSP不能夠停下來,而需要從主機中實時地讀取數據或者向主機實時地輸出數據。德州儀器的C5000,C6000系列的DSP都可以通過JTAG接口來實現這個功能。   

    RTDX在主機端可以與任何符合OLE接口的應用程序交換數據。例5是一個使用RTDX在主機和DSP之間進行數據傳遞的例子。主機端是一個基於VB的小程序。
  例5  DSP程序:
  #include<rtdx.h>
  RTDX_CreateInputChannel(writeload);
  RTDX_CreateOutputChannel(readload);

  int  main()
  {
  RTDX_enableInput(&writeload);
  RTDX_enableOutput(&readload);
  return;
  }

  void  doExchange()
  {
  if(!RTDX_channelBusy(&writeload)){
  RTDX_readNB(&writeload、&loadVoal、sizeof(load-Val));
  }
  RTDX_write(&readload、&loadVal、sizeof(loadVal));
  }
  使用VB編制的主機端程序
  set  r=CreateObject(“RTDX”)
  status=r.open(“readload”、“R”)
  set  w=CreateObject(“RTDX”)
  status=w.open(“writeload”、“W”)
  status=r.ReadI4(data)
  status=w.WriteI4(value、bufferstate)
  綜上所述,DSP/BIOS針對DSP的應用環境,通過一系列的對象模塊向開發者提供了一個實用優秀的實時操作系統。它可以幫助用戶提高軟件的模塊化、並行性和維護性等,有利於降低系統成本和縮短開發週期。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章