(原創) 如何從Nios II讀出CMOS放在SDRAM中的影像? (SOC) (SOPC Builder) (Nios II) (DE2-70) (TRDB-D5M) (TRDB-LTM)

Abstract
本文提供一個CMOS Controller,讓Nios II可以藉由CMOS Controller控制CMOS,並能讀出CMOS放在SDRAM中的影像。

Introduction
使用環境:Quartus II 8.0 SP1 + Nios II EDS 8.0 SP1 + DE2-70 (Cyclone II EP2C70F896C6N) + TRDB-D5M + TRDB-LTM

(很多人問我為什麼這篇不寫完,並不是這篇做法不對,主要有兩個原因:1.這篇要寫完很長,有點懶,2.後來弄出了master架構的CMOS與LTM,比這個slave架構更好,因為正在測試中,等最後穩定了再發表,想到時候再一併補齊本文作比較,不過我已經先放了source code,有興趣的朋友可自行參考研究。)

利用DE2做電腦視覺應用的,大概分3種族群:第1種是做純硬的,把DE2當FPGA用,如(原創) 如何實現Real Time的Sobel Edge Detector? (SOC) (Verilog) (Image Processing) (DE2-70) (TRDB-D5M),完全拼Verilog與RTL;第2種是做純軟的,把DE2當成嵌入式Nios II平台,拼的是C語言與OS;第3種是軟硬體設計,Verilog與C通通來,軟體硬體都要弄。

DE2的DE2_CCD與DE2-70的DE2_70_D5M_LTM,都是純硬的範例,若要拿來做Nios II嵌入式與軟硬體設計,大家最常問的問題是:

『我該怎麼在Nios II去控制CMOS?』
『我該怎麼在Nios II讀出CMOS放在SDRAM中的影像?』

要讓Nios II去控制硬體,必須要透過controller,Altera與友晶科技都已經提供不少現成的controller,以前在Quartus II 6.1更有鼎鼎有名的Altera University IP Core可用,但無論是130萬像素的TRDB-DC2或者500萬像素的TRDB-D5M,目前都沒看到給Nios II用的CMOS controller,我在2008年初的3篇

(原創) 如何在DE2將CCD影像顯示在彩色LCD上? (純硬體篇) (IC Design) (DE2)
(原創) 如何在DE2將CCD影像顯示在彩色LCD上? (Nios II軟體篇 + onchip memory) (IC Design) (DE2) (Nios II) (SOPC Builder)
(原創) 如何在DE2將CCD影像顯示在彩色LCD? (Nios II軟體篇 + μC/OS-II + SRAM + 驅動程式) (IC Design) (DE2) (Nios II) (μC/OS-II) (SOPC Builder)

曾經試圖為130萬像素的TRDB-DC2寫一個給Nios II用的controller,不過當時功力有限,只能寫出控制CMOS的free run、capture、與exposure三個功能,讀出CMOS放在SDRAM中的影像從這部分一直弄不出來,期間試過很多方法,好幾次接近成功,雖然能從SDRAM讀出資料,不過讀出的資料卻始終是錯的。

以下這張圖片,並不是如(原創) 如何將CMOS所擷取的影像傳到PC端? (IC Design) (DE2)所言,利用友晶科技提供的Control Panel與Image Converter產生,而是在Nios II利用C語言,從SDRAM讀出每個pixel的RGB,自己fwrite()出800 * 400的bmp檔

nios_capture00

這代表什麼意義呢?
這表示在Nios II能正確地讀出CMOS放在SDRAM中的影像,進而利用C語言做更複雜的演算法處理!!

系統架構圖

nios_capture01

紫色部分是大家熟悉的DE2_CCD架構(DE2 + 130萬像素CMOS + 640 * 480 VGA),其他如DE2_LCM_CCD (DE2 + 130萬像素CMOS + 320 * 240 3.6" LCM)與DE2_70_D5M_LTM (DE2-70 + 500萬像素CMOS + 800 * 480 4.3" LTM) 基本上用的仍是這個架構。

I2C Sensor Configuration(I2C_CCD_Config.v)
負責設定CMOS的register。

CMOS Sensor Data Capture(CCD_Capture.v)
負責接收CMOS所傳來的Bayer Pattern格式資料。

Bayer Color Pattern Data to 30-Bit RGB (RAW2RGB.v)
負責將Bayer Pattern格式轉成RGB格式。

Multi-Port SDRAM Controller(Sdram_Control_4Port.v)
負責讀寫SDRAM。SDRAM為CMOS與VGA(LCM/LTM)之間的frame buffer,它一共有4個port:2 read port與2 write port,每個port為16 bit。

LTM Controller and Data Request(VGA_Controller.v/LCM_Controller.v/touch_tcon.v)
負責將SDRAM的RGB資料送進VGA(LCM/LTM)顯示。

這些Verilog的RTL code相當精采,我將會另開篇幅詳細討論每個module中的code。

加入Nios II所面臨的困難
1.無法在Nios II使用CMOS
無論是DE2_CCDDE2_LCM_CCDDE2_70_D5M_LTM範例,這些都是純硬的RTL code,若要讓純軟的C介入使用CMOS,就必須使用SOPC Builder加上Nios II CPU以及相關周邊的controller,在(原創) 哪裡有DE2-70的Nios II reference design可以參考? (SOC) (DE2-70) (Nios II) (SOPC Builder),我們在DE2-70已經可以用Nios II驅動絕大部分周邊,但因為缺少CMOS controller,所以目前我們還無法在Nios II使用CMOS。

2.無法在Nios II存取SDRAM
大家都知道CMOS所capture的影像放在SDRAM,理論上只要能在Nios II讀出CMOS放在SDRAM中的影像,就能用C做更複雜的演算法處理,但因為在DE2_CCD架構中,SDRAM已經被當成frame buffer,用的是Sdram_Control_4Port.v這個4 port的SDRAM controller,而Nios II用的是SOPC Builder上的SDRAM controller,因為兩者無法並存(請問top module的SDRAM port該接Sdram_Control_4Port?還是接Nios II?),所以目前我們還無法在Nios II存取SDRAM。

本文所提供的解決方案
如系統架構圖所示,本文的重點就是黃色部分:提供一個CMOS Controller,讓Nios II可以藉由CMOS Controller控制CMOS,並能讀出CMOS放在SDRAM中的影像,最後將這些影像資訊放在SSRAM中,讓C語言能做更複雜的演算法處理。

CMOS Controller
CMOS_Controller.v / Verilog

1 /* 
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3 
4 Filename    : CMOS_Controller.v
5 Compiler    : Quartus II 7.2 SP3
6 Description : Demo how to write CMOS Controller
7 Release     : 08/31/2008 1.0
8 */
9 
10 module CMOS_Controller (
11   // Avalon clock interface siganals
12   input               csi_clockreset_clk,
13   input               csi_clockreset_reset_n,
14   // Signals for Avalon-MM slave port
15   input      [1:0]    avs_s1_address,
16   input               avs_s1_chipselect,
17   input               avs_s1_read,
18   output reg [31:0]   avs_s1_readdata,
19   input               avs_s1_write,
20   input      [31:0]   avs_s1_writedata,
21   // Signals export to top module
22   output              avs_s1_export_clk,
23   output reg          avs_s1_export_capture_start,
24   output reg          avs_s1_export_capture_stop,
25   output reg          avs_s1_export_capture_read,
26   input      [31:0]   avs_s1_export_capture_readdata
27 );
28 
29 // Slave address constant
30 parameter CAPTURE_START = 2'h0;
31 parameter CAPTURE_STOP  = 2'h1;
32 parameter CAPTURE_DATA  = 2'h2;
33 
34 assign avs_s1_export_clk = ~csi_clockreset_clk;
35 
36 // write to export
37 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
38   if (!csi_clockreset_reset_n) begin
39     avs_s1_export_capture_start <= 1'b0;
40     avs_s1_export_capture_stop  <= 1'b0;
41   end
42   else begin
43     if (avs_s1_chipselect && avs_s1_write) begin
44       case (avs_s1_address)
45         CAPTURE_START:
46           avs_s1_export_capture_start <= avs_s1_writedata[0];
47        
48         CAPTURE_STOP:
49           avs_s1_export_capture_stop  <= avs_s1_writedata[0];
50        
51         default: begin
52           avs_s1_export_capture_start <= avs_s1_export_capture_start;
53           avs_s1_export_capture_stop  <= avs_s1_export_capture_stop;
54         end
55       endcase
56     end
57   end
58 end
59 
60 // read from export
61 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
62   if (!csi_clockreset_reset_n) begin
63     avs_s1_export_capture_read <= 1'b0;
64     avs_s1_readdata            <= 32'hzzzzzzzz;
65   end
66   else begin
67     avs_s1_export_capture_read <= 1'b0;
68     avs_s1_readdata            <= 32'hzzzzzzzz;
69    
70     if (avs_s1_chipselect && avs_s1_read) begin
71       case (avs_s1_address)
72         CAPTURE_DATA: begin
73           avs_s1_export_capture_read <= 1'b1;
74           avs_s1_readdata            <= avs_s1_export_capture_readdata;
75         end
76         default: begin
77           avs_s1_export_capture_read <= avs_s1_export_capture_read;
78           avs_s1_readdata            <= avs_s1_readdata;
79         end
80       endcase
81     end
82   end                    
83 end
84 
85 endmodule


11行

// Avalon clock interface siganals
input               csi_clockreset_clk,
input               csi_clockreset_reset_n,


使用clock interface,定義controller所使用的clock與reset,由Avalon bus傳入。

14行

// Signals for Avalon-MM slave port
input      [1:0]    avs_s1_address,
input               avs_s1_chipselect,
input               avs_s1_read,
output reg [31:0]   avs_s1_readdata,
input               avs_s1_write,
input      [31:0]   avs_s1_writedata,


使用Avalon MM slave interface, 是CMOS controller與Avalon bus溝通所使用的port。
avs_s1_address
由Avalon bus傳入,是CMOS controller在SOPC Builder定址空間的offset,單位為word,用此address判斷readdata與writedata上的資料是什麼信號。

avs_s1_chipselect
由Avalon bus傳入,因為Avalon bus上會有很多master與slave,所以controller在writedata所收到信號,可能是要傳給其他slave的信號,而不是自己該處理的信號,因此Avalon bus會根據adress去decode,產生chipselect,所以slave就不需再對所有信號去decode,只需簡單判斷chipselect是否為1,就可確定是否屬於自己該處理的信號。

avs_s1_read
avs_s1_readdata

read由Avalon bus傳入,表示Avalon bus對slave有讀取資料的需求,然後slave會從SDRAM讀取資料,藉由readdata傳給Avalon bus。

avs_s1_write
avs_s1_writedata

write與writedata由Avalon bus傳入,表示Avalon bus對slave有寫入資料的需求,並藉由writedata將Nios II控制CMOS的capture start與capture stop的信號傳給slave。

21行

// Signals export to top module
output              avs_s1_export_clk,
output reg          avs_s1_export_capture_start,
output reg          avs_s1_export_capture_stop,
output reg          avs_s1_export_capture_read,
input      [31:0]   avs_s1_export_capture_readdata


CMOS controller所扮演的腳色,本來就是Avalon bus與原本CCD_Capture、Sdram_Control_4Port之間的adapter,所以除了面對Avalon bus的port外,也要有能面對原本CCD_Capture與Sdram_Control_4Port的port,export就屬於這種port。
avs_s1_export_clk
傳出到top module,將此clock傳進Sdram_Control_4Port。

avs_s1_export_capture_start
傳出到top module,由Nios II傳入的capture start信號將由此port傳給CCD_Capture。

avs_s1_exort_capture_stop
傳出到top module,由Nios II傳入的capture stop信號將由此port傳給CCD_Capture。

avs_s1_export_capture_read
傳出到top module,由Avalon bus傳入的read信號將由此port傳給Sdram_Control_4Port的read enable。

avs_s1_export_capture_readdata
由top module傳入,SDRAM的資料藉由此port傳給slave,然後再由readdata傳回Nios II。

以上port的命名方式皆根據(筆記) Naming Convention for Avalon Signal Type (IC Design) (SOPC Builder)所建議的naming convention命名, 除了可讀性較高外,SOPC Builder也可以自動抓到signal type,不需再另外設定。

29行

// Slave address constant
parameter CAPTURE_START = 2'h0;
parameter CAPTURE_STOP  = 2'h1;
parameter CAPTURE_DATA  = 2'h2;


設定所有slave address會用到的位址常數,由於Avalon bus與slave交換資料皆靠readdata與writedata,以傳入slave的writedata為例,該如何判斷傳進來的是capture start?還是capture stop呢? 就是靠address判斷,若為2'h0,就是capture start,若為2'h1,則為capture stop。

在CMOS_Controller_HAL.h中,我們也可以看到完全相同的設定,供Nios II呼叫CMOS controller時使用。

// slave address
#define CAPTURE_START      0x0
#define CAPTURE_STOP       0x1
#define CAPTURE_DATA       0x2


36行

// write to export
always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
 
if (!csi_clockreset_reset_n) begin
    avs_s1_export_capture_start
<= 1'b0;
    avs_s1_export_capture_stop  <= 1'b0;
  end
 
else begin
   
if (avs_s1_chipselect && avs_s1_write) begin
     
case (avs_s1_address)
        CAPTURE_START:
          avs_s1_export_capture_start
<= avs_s1_writedata[0];
       
        CAPTURE_STOP:
          avs_s1_export_capture_stop 
<= avs_s1_writedata[0];
       
       
default: begin
          avs_s1_export_capture_start
<= avs_s1_export_capture_start;
          avs_s1_export_capture_stop 
<= avs_s1_export_capture_stop;
       
end
     
endcase
   
end
 
end
end


將從Avalon bus讀進的writedata資料,經過處理寫到export,最後傳給CCD_Capture。為什麼slave要做這些判斷呢?這要從slave write的timing diagram來理解。

nios_capture02

以上是write wait為0時的timing diagram,這也是建立custom component時,SOPC Builder預設的write wait。

為什麼設定write wait為0,而不是1或2呢?
根據Altera Avalon Memory-Mapped Interface Specification p.35的解釋:

The fundamental write transfer is generally appropriate for synchronous, on-chip peripherals that can capture data in a single clock cycle. Peripherals that cannot capture data in one clock cycle must use wait-states.


所謂的fundamental write,就是write wait為0,因為要寫進slave的對象是Nios II CPU,與slave就在同一個FPGA上,屬於on-chip peripherals,所以使用write wait為0即可。

更詳細的write wait與read wait討論,請參考(原創) 如何設定Avalon Slave Timing? (SOC) (SOPC Builder)

// read from export
always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
 
if (!csi_clockreset_reset_n) begin
    avs_s1_export_capture_read
<= 1'b0;
    avs_s1_readdata            <= 32'h0000;
  end
 
else begin
    avs_s1_export_capture_read
<= 1'b0;
    avs_s1_readdata            <= 32'hzzzzzzzz;
   
   
if (avs_s1_chipselect && avs_s1_read) begin
     
case (avs_s1_address)
        CAPTURE_DATA:
begin
          avs_s1_export_capture_read
<= 1'b1;
          avs_s1_readdata            <= avs_s1_export_capture_readdata;
       
end
       
       
default: begin
          avs_s1_export_capture_read
<= 1'b0;
          avs_s1_readdata            <= 32'hzzzzzzzz;
        end
     
endcase
   
end
 
end                    
end

 

 

assign avs_s1_export_clk = ~csi_clockreset_clk;

 

 

(未完...)

在DE2-70實現

證明此方案的正確性

實務上的應用


在DE2實現

其他方法

完整程式碼下載
DE2_70_D5M_LTM_NIOS_capture.7z

Conclusion

See Also
(原創) 如何實現Real Time的Sobel Edge Detector? (SOC) (Verilog) (Image Processing) (DE2-70) (TRDB-D5M)
(原創) 如何在DE2將CCD影像顯示在彩色LCD上? (純硬體篇) (IC Design) (DE2)
(原創) 如何在DE2將CCD影像顯示在彩色LCD上? (Nios II軟體篇 + onchip memory) (IC Design) (DE2) (Nios II) (SOPC Builder)
(原創) 如何在DE2將CCD影像顯示在彩色LCD? (Nios II軟體篇 + μC/OS-II + SRAM + 驅動程式) (IC Design) (DE2) (Nios II) (μC/OS-II) (SOPC Builder)
(原創) 如何將CMOS所擷取的影像傳到PC端? (IC Design) (DE2)
(原創) 哪裡有DE2-70的Nios II reference design可以參考? (SOC) (DE2-70) (Nios II) (SOPC Builder)
(筆記) Naming Convention for Avalon Signal Type (IC Design) (SOPC Builder)
(筆記) Quartus II 7.x版的Avalon Memory-Mapped Interface Specification分享 (SOC) (SOPC Builder)
(原創) 如何設定Avalon Slave Timing? (SOC) (SOPC Builder)
(原創) 如何在Nios II EDS 8.0使用Host File System與Zip File System? (SOC) (Nios II)

Reference
Altera Avalon Memory-Mapped Interface Specification

posted on 2008-10-24 01:57  真 OO无双  阅读(20961)  评论(97编辑  收藏  举报

导航