(原創) 如何設計一個七段顯示器Controller? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)

Abstract
在上一篇blog,我們已經學會如何將Altera內建的controller加上SOPC Builder,並且用軟體來控制硬體,儘管如此,你會發現在DE2-70上,仍然有很多硬體還沒被驅動,如VGA、LTM、CMOS、Ethernet、Audio CODEC、七段顯示器...等,此外,雖然現在軟體能控制硬體了,卻必須依賴Altera所提供的controller,只要Altera沒提供controller,我們就沒辦法去控制該硬體。在本文中,我們將自己實做出一個Altera沒提供的controller:七段顯示器controller,使七段顯示器能被Nios II軟體所控制,其中包含硬體controller與軟體HAL的開發。

Introduction
使用環境:Quartus II 8.1 + Nios II EDS 8.1 + DE2-70 (Cyclone II EP2C70F896C6N)

這4篇原本是設計在一起的lab,適合初學者從0開始慢慢熟悉Quartus II、SOPC Builder、Nios II EDS、Avalon Bus Slave、Avalon Bus Master。

(原創) 如何自己用SOPC Builder建立一個能在DE2-70上跑μC/OS-II的Nios II系統? (SOC) (Quartus II) (SOPC Builder) (Nios II) (μC/OS-II) (DE2-70)
(原創) 如何設計一個七段顯示器Controller? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個SD卡Wav Player? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個數位相框? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)

由於Nios II CPU是掛在Avalon Bus上,若要讓七段顯示器被Nios II所控制,我們就得寫一個七段顯示器Controller也掛在Avalon Bus上,如下圖所示:

seg7_00

SEG7 Controller該如何掛在Avalon Bus呢?
在Altera的Avalon Interface Specification中,定義了各種interface,只要我們將controller實做了這些interface,就能將controller掛在Avalon Bus上。在Quartus II 8.1中,Altera定義了非常多種interface可以使用,各有各的優點與特色。由於七段顯示器是個慢速的裝置,而且主要是受Nios II CPU這個master所控制,因此我們選擇了Avalon-MM slave interface

Master Interface vs. Slave Interface
在Avalon-MM的世界中,主要分Master Interface與Slave Interface,簡單的說,Master能夠自己發起傳輸,Slave則必須由Master控制而被動的發起傳輸,在Lab 2中,SEG7 Controller是由Nios II CPU這個master所控制,而不能主動發起傳輸,所以用的是Slave Interface

seg7_01

Step 1:
使用lab2_files.7z內的seg7_controller project

你也可以使用在(原創) 如何自己用SOPC Builder建立一個能在DE2-70上跑μC/OS-II的Nios II系統? (SOC) (Quartus II) (SOPC Builder) (Nios II) (μC/OS-II) (DE2-70)hello_ucosii.7z繼續開發。

Step 2:
建立SEG7_Controller目錄架構

一個典型的ip(ip = controller),其目錄架構如下圖所示。在C:\altera\81\ip\altera\sopc_builder_ip\目錄下,有很多Altera所提供的ip,都是用這種目錄結構存放。

外層的inc,放的是俗稱的register map,是由macro所構成,以macro的方式直接存取硬體。
HAL的inc,放是HAL的header files,而src則放HAL的source code。

至於要不要寫register map呢?Altera是建議你要寫,不過若你偷懶的話,可以不寫register map,只提供HAL即可,這樣Nios II一樣可以使用你寫的ip。

seg7_03

 

在Altera的Quartus II Handbook Vol 4: SOPC Builder建議,將所有自己寫的ip放在project的ip目錄下,如此SOPC Builder啟動時,會自動載入該目錄下所有ip。我們依照以上的標準為Seg7 Controller建立如下的目錄結構。(Lab 2我們省略了register map,直接建立HAL,所以忽略了外層的inc目錄)。

seg7_04

Step 3:
開發SEG7_Controller.v

lab2_files.7z的SEG7_Controller.v複製到C:\DE2-70\seg7_controller\ip\SEG7_Controller\下,如同Lab 1一樣,SEG7_Controller.v是個半成品,我將重要的code加上了馬賽克,需要同學來完成。

SEG7_Controller.v / Verilog

1 module SEG7_Controller (
2 // Avalon clock interface siganals
3   /* Todo!! add clock interface signals
4 */
5 // Signals for Avalon-MM slave port
6   /* Todo!! add Avalon-MM slave interface signals
7 *
8 */
9 // user logic inputs and outputs
10   /* Todo!! add user logic inputs and outputs signals */
11 );
12
13  // avs_s1_address: index used to specfied seg7 unit,
14  // 0 means first seg7 unit
15  // 1 menas second seg7 unit...
16
17  // avs_s1_writedata: 8 bits map to seg7 as beblow (1:on, 0:off)
18 /*
19 0
20 -------
21 | |
22 5| 6 |1
23 -------
24 | |
25 4| |2
26 ------- . 7
27 3
28  */
29
30  parameter DATA_WIDTH = 32; // data bus width
31  parameter SEG7_NUM = 8; // specify the number of seg7 unit
32  parameter ADDR_WIDTH = 3; // log2(SEG7_NUM)
33  
34  reg [SEG7_NUM-1:0] base_index;
35  reg [DATA_WIDTH-1:0] write_data;
36  reg [(SEG7_NUM*8-1):0] reg_file;
37
38  // Todo!! assgin output to user logic output
39  
40  always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
41 if (!csi_clockreset_reset_n) begin
42 integer i;
43 base_index <= 8'h00;
44   write_data <= 8'h00;
45  
46 for(i = 0; i < SEG7_NUM * 8; i = i + 1)
47 reg_file[i] <= 1'b1;
48   end
49 else begin
50 /*
51 * Todo!! reference Avalon-MM slave write timing diagram
52 *
53 *
54 *
55 *
56 *
57 */
58 end
59  end
60
61  endmodule

Avalon-MM Interface Specification and Slave Write Transfer Timing Diagram
在開發SEG7 Controller之前,首先必須了解Avalon-MM Slave Interface Spec以及其timing是如何運作。

seg7_02 

(A) 第一個周期在clk上升沿開始。
(B) Avalon Bus發出有效的address、byteenable、write、writedata信號。
(D) Slave IP在clk上升沿捕捉address、byteenable、write、writedata。整個傳輸在1個clk完成,下一個clk可以發起另一次傳輸。

有以上的波型圖我們可以發現,一個Avalon-MM Slave Transfer最少必須包含以下信號
clk
address
byteenable
write
writedata
並且在1個clk內完成傳輸。

更詳細的資料,請參考Avalon-MM Interface Specification

現在我們開始將SEG7_Controller.v被馬賽克的部分補齊。

第2行到第8行,要填入Avalon-MM Slave Interface,由上圖得知共有clk、address、byteenable、write、writedata等5個信號,由於我們配合Nios II CPU的32 bit databbus,七段顯示器也採用32 bit傳輸,所以可忽略byteenable信號,當忽略byteenable信號時,表示32 bit全部enable。因此最後剩下clk、address、write、writedata 4個信號。

在Quartus II 7.2之後,建議將clk與reset獨立成Clock Interface,所以原來的Avalon-MM slave interface剩下address、write與writedata。

理論上,這4個信號的名稱可以自己任意取,不過Altera建議依照以下的naming convention命名。

seg7_05

seg7_06

依照這樣的naming convention有2個好處:
1.大家依照相同的命名規則,程式可讀性較高。
2.SOPC Builder會自動得知每個信號的interface type與singnal type,不需每個port一一指定。(稍後會發現這種寫法的優點)。

第2行到第10行,請填上

module SEG7_Controller (
// Avalon clock interface siganals
input csi_clockreset_clk,
input csi_clockreset_reset_n,
// Signals for Avalon-MM slave port
input [ADDR_WIDTH-1:0] avs_s1_address,
input avs_s1_write,
input [DATA_WIDTH-1:0] avs_s1_writedata,
// user logic inputs and outputs
output [SEG7_NUM*8-1:0] avs_s1_export_oHEX
);

其中avs_s1_export_oHEX並非Avalon-MM interface,而是要與top module DE2_70_NIOS.v相接。

50行到57行請填上

if (avs_s1_write) begin
integer j;
write_data
<= avs_s1_writedata;
base_index
<= (avs_s1_address << 3);

for(j = 0; j < 8; j = j + 1)
reg_file[base_index
+j] <= write_data[j];
end

根據slave write transfer timing diagram,write是writedata是否為合法信號的判斷依據,所以加上了if (avs_s1_address)判斷。

address表示第n顆七段顯示器,由於一個7段顯示器占8 bit,base_index相當於avs_s1_address * 8,也就是avs_s1_address << 3。

reg_file是8個七段顯示器的vector。

38行請填上

assign avs_s1_export_oHEX = ~reg_file;

將reg_file送到top module。因為七段顯示器是low active,也就是0才會亮,所以加上了~。

最後完整程式如下:
SEG7_Controller.v / Verilog

1 /*
2 (C) OOMusou 2010 http://oomusou.cnblogs.com
3
4 Filename : SEG7_Controller.v
5 Simulator : Quartus II 8.1
6 Description : SEG7 Controller for Avalon bus
7 Release : Aug.12,2010 1.0
8  */
9
10 module SEG7_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 [ADDR_WIDTH-1:0] avs_s1_address,
16 input avs_s1_write,
17 input [DATA_WIDTH-1:0] avs_s1_writedata,
18 // user logic inputs and outputs
19   output [SEG7_NUM*8-1:0] avs_s1_export_oHEX
20 );
21
22  // avs_s1_address: index used to specfied seg7 unit,
23  // 0 means first seg7 unit
24  // 1 menas second seg7 unit...
25
26  // avs_s1_writedata: 8 bits map to seg7 as beblow (1:on, 0:off)
27 /*
28 0
29 -------
30 | |
31 5| 6 |1
32 -------
33 | |
34 4| |2
35 ------- . 7
36 3
37  */
38
39 parameter DATA_WIDTH = 32; // data bus width
40  parameter SEG7_NUM = 8; // specify the number of seg7 unit
41  parameter ADDR_WIDTH = 3; // log2(SEG7_NUM)
42  
43 reg [SEG7_NUM-1:0] base_index;
44 reg [DATA_WIDTH-1:0] write_data;
45 reg [(SEG7_NUM*8-1):0] reg_file;
46
47 assign avs_s1_export_oHEX = ~reg_file;
48
49 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
50 if (!csi_clockreset_reset_n) begin
51 integer i;
52 base_index <= 8'h00;
53   write_data <= 8'h00;
54  
55 for(i = 0; i < SEG7_NUM * 8; i = i + 1)
56 reg_file[i] <= 1'b1;
57   end
58 else begin
59 if (avs_s1_write) begin
60 integer j;
61 write_data <= avs_s1_writedata;
62 base_index <= (avs_s1_address << 3);
63
64 for(j = 0; j < 8; j = j + 1)
65 reg_file[base_index+j] <= write_data[j];
66 end
67 end
68 end
69
70 endmodule

Step 5:
開發HAL

Lab2_files.zip
SEG7.h複製到C:\DE2-70\seg7_controller\ip\SEG7_Controller\HAL\inc下,
SEG7.c複製到C:\DE2-70\seg7_controller\ip\SEG7_Controller\HAL\src下。

SEG7.h / C

1 #ifndef SEG7_H_
2  #define SEG7_H_
3
4 #include "alt_types.h" // alt_u32, alt_u8
5
6  // all clear
7  void SEG7_Clear(void);
8  // all full
9  void SEG7_Full(void);
10  // display 0 1 2 3 4 5 6 7
11  void SEG7_Number(void);
12  // display by decimal
13 void SEG7_Decimal(alt_u32 Data, alt_u8 point_mask);
14 // display by hex
15 void SEG7_Hex(alt_u32 Data, alt_u8 point_mask);
16
17
18
19 #endif /*SEG7_H_*/
20

這是HAL的header file,定義了所提供的API。

SEG7.c / C (是個半成品,我將重要的code加上了馬賽克,需要同學來完成)

1 #include "system.h"
2 #include "alt_types.h" // alt_u8
3 #include "io.h" // IOWR()
4 #include "SEG7.h"
5
6 // Todo!! register map
7 #define SEG7_NUM 8
8
9 /*
10 0
11 -------
12 | |
13 5| 6 |1
14 -------
15 | |
16 4| |2
17 ------- . 7
18 3
19 */
20
21 /* Todo!!!
22 SEG7 digit map
23
24 */
25
26 // all clear
27 void SEG7_Clear(void) {
28 int i;
29
30 for(i=0; i<SEG7_NUM; i++)
31 SEG7_SET(i, 0x00);
32 }
33
34 // all full
35 void SEG7_Full(void) {
36 int i;
37
38 for(i=0; i<SEG7_NUM; i++)
39 SEG7_SET(i, 0xFF);
40 }
41
42 // display 0 1 2 3 4 5 6 7
43 void SEG7_Number(void) {
44 int i;
45
46 for(i=0;i<SEG7_NUM;i++)
47 SEG7_SET(i, szMap[i]);
48 }
49
50 // display by decimal
51 void SEG7_Decimal(alt_u32 Data, alt_u8 point_mask) {
52 alt_u8 mask = 0x01;
53 alt_u8 seg_mask;
54 int i;
55 seg_mask = 0;
56
57 for(i = 0; i < SEG7_NUM; i++) {
58 seg_mask = szMap[Data%10];
59 Data /= 10;
60
61 if (point_mask & mask)
62 seg_mask |= 0x80;
63
64 mask <<= 1;
65 SEG7_SET(i, seg_mask);
66 }
67 }
68
69 // display by hex
70 void SEG7_Hex(alt_u32 Data, alt_u8 point_mask) {
71 alt_u8 mask = 0x01;
72 alt_u8 seg_mask;
73 int i;
74
75 seg_mask = 0;
76 for(i = 0; i < SEG7_NUM; i++) {
77 seg_mask = szMap[Data & 0x0F];
78 Data >>= 4;
79
80 if (point_mask & mask)
81 seg_mask |= 0x80;
82
83 mask <<= 1;
84 SEG7_SET(i, seg_mask);
85 }
86 }

第6行, 請填上

#define SEG7_SET(index, seg_mask) IOWR(SEG7_BASE, index, seg_mask)

這就是register map,直接控制第n顆七段顯示器的顯示,index表第n顆,seg_mask表示顯示的數字。

Altera是建議register map要另外寫,但由於在此範例只有1行而以,我就直接寫在HAL內了。

第21行到24行,請填上

static unsigned char szMap[] = {
63, 6, 91, 79, 102, 109, 125, 7,
127, 111, 119, 124, 57, 94, 121, 113
};
// 0,1,2,.9, a, b, c, d, e, f

這個陣列,放的是七段顯示器每個數字的lookup table。

/*
0
-------
| |
5| 6 |1
-------
| |
4| |2
------- . 7
3
*/

拿0做舉例,要讓0亮,依上圖就是0、1、2、3、4、5必須為1,也就是8'b0001_1111,換算成10進位就是63,其他的數字同理,同學可自行推導。

最後完整的程式如下:
SEG7.c / C

1 /*
2 (C) OOMusou 2010 http://oomusou.cnblogs.com
3
4 Filename : SEG7.c
5 Compiler : Nios II EDS 8.1
6 Description : SEG7 Controller HAL for Avalon bus
7 Release : Aug.12,2010 1.0
8 */
9 #include "system.h"
10 #include "alt_types.h" // alt_u8
11 #include "io.h" // IOWR()
12 #include "SEG7.h"
13
14 #define SEG7_SET(index, seg_mask) IOWR(SEG7_BASE, index, seg_mask)
15 #define SEG7_NUM 8
16
17 /*
18 0
19 -------
20 | |
21 5| 6 |1
22 -------
23 | |
24 4| |2
25 ------- . 7
26 3
27 */
28
29 static unsigned char szMap[] = {
30 63, 6, 91, 79, 102, 109, 125, 7,
31 127, 111, 119, 124, 57, 94, 121, 113
32 }; // 0,1,2,....9, a, b, c, d, e, f
33
34 // all clear
35 void SEG7_Clear(void) {
36 int i;
37
38 for(i=0; i<SEG7_NUM; i++)
39 SEG7_SET(i, 0x00);
40 }
41
42 // all full
43 void SEG7_Full(void) {
44 int i;
45
46 for(i=0; i<SEG7_NUM; i++)
47 SEG7_SET(i, 0xFF);
48 }
49
50 // display 0 1 2 3 4 5 6 7
51 void SEG7_Number(void) {
52 int i;
53
54 for(i=0;i<SEG7_NUM;i++)
55 SEG7_SET(i, szMap[i]);
56 }
57
58 // display by decimal
59 void SEG7_Decimal(alt_u32 Data, alt_u8 point_mask) {
60 alt_u8 mask = 0x01;
61 alt_u8 seg_mask;
62 int i;
63 seg_mask = 0;
64
65 for(i = 0; i < SEG7_NUM; i++) {
66 seg_mask = szMap[Data%10];
67 Data /= 10;
68
69 if (point_mask & mask)
70 seg_mask |= 0x80;
71
72 mask <<= 1;
73 SEG7_SET(i, seg_mask);
74 }
75 }
76
77 // display by hex
78 void SEG7_Hex(alt_u32 Data, alt_u8 point_mask) {
79 alt_u8 mask = 0x01;
80 alt_u8 seg_mask;
81 int i;
82
83 seg_mask = 0;
84 for(i = 0; i < SEG7_NUM; i++) {
85 seg_mask = szMap[Data & 0x0F];
86 Data >>= 4;
87
88 if (point_mask & mask)
89 seg_mask |= 0x80;
90
91 mask <<= 1;
92 SEG7_SET(i, seg_mask);
93 }
94 } 

Step 6:
複製component.mk

Lab2_files.zip的component.mk複製到C:\DE2-70\seg7_controller\ip\SEG7_Controller\HAL\src下。

component.mk

# List all source files supplied by this component.

C_LIB_SRCS
+= SEG7.c

ASM_LIB_SRCS
+=

這是個很重要的步驟,若缺少這個步驟,將來Nios II EDS在編譯時會抓不到SEG7.c而無法編譯成功,必須手動將SEG7.c複製到軟體的project才行。在Altera文件甚至友晶科技的範例都沒提到這點,主要是這個工作在Quartus II 6.1之前,SOPC Builder會自動幫你做,也就是會自動產生component.mk,但在Quartus II 7.0之後,將這個功能廢除了,這的動作只好我們自己做。

Step 7:
使用SOPC Builder將IP打包

該寫的code都寫好了,不過這還不夠,SOPC Builder還無法認識這個ip,必須透過SOPC Builder將以上寫的code打包成ip,SOPC Builder才能識別。

File -> New Component

seg7_07 

在HDL Files tab,按下Add...加入SEG7_Controller.v

seg7_08 

seg7_09

在Signals tab,我們會發現SOPC Builder已經自動抓到Interface與Signal Type,不需再一一指定了,這就是使用naming convention的優點。

seg7_10

在Interfaces tab,SOPC Builder根據我們的naming convention,自動抓出了3個interface:clockreset、s1與export_s1,可分別做設定。由於這個controller僅僅使用最簡單的Slave Write,並且在1個clk完成,所以Write Wait要填0,表示zero wait,1個clk內可以完成slave write transfer。

seg7_11

在Compnent Wizard tab,我們看到最後的設定,若有任何錯誤,最下方會有錯誤訊息。最後按Finish完成。

seg7_12

按Yes, Save存檔。

seg7_13

最後在右側,SOPC Builder會顯示SEG7_Controller,表示新的ip順利打包成功。

seg7_14

Step 8:
加入SEG7 Controller

seg7_15

將SEG7_Controller_0改成seg7,最後按下Generate產生SOPC system。

seg7_16

Step 9:
修改Top Module

將seg7_controller.v / Verilog的300行加入

// the_seg7
.avs_s1_export_oHEX_from_the_seg7({oHEX7_DP, oHEX7_D, oHEX6_DP, oHEX6_D, oHEX5_DP,
oHEX5_D, oHEX4_DP,oHEX4_D, oHEX3_DP, oHEX3_D, oHEX2_DP, oHEX2_D, oHEX1_DP, oHEX1_D,
oHEX0_DP, oHEX0_D})
// 7-SEG Dispaly

完整的top module如下

seg7_controller.v / Verilog

1 /*
2 (C) OOMusou 2010 http://oomusou.cnblogs.com
3
4 Filename : seg7_controller.v
5 Simulator : Quartus II 8.1
6 Description : SEG7 Controller for Avalon bus demo top module
7 Release : Aug.12,2010 1.0
8 */
9
10 module seg7_controller (
11 input iCLK_28, // 28.63636 MHz
12 input iCLK_50, // 50 MHz
13 input iCLK_50_2, // 50 MHz
14 input iCLK_50_3, // 50 MHz
15 input iCLK_50_4, // 50 MHz
16 input iEXT_CLOCK, // External Clock
17 ////////////////////////////// Push Button ////////////////////////
18 input [3:0] iKEY, // Pushbutton[3:0]
19 ////////////////////////////// DPDT Switch ////////////////////////
20 input [17:0] iSW, // Toggle Switch[17:0]
21 ////////////////////////////// 7-SEG Dispaly ////////////////////////
22 output [6:0] oHEX0_D, // Seven Segment Digit 0
23 output oHEX0_DP, // Seven Segment Digit 0 decimal point
24 output [6:0] oHEX1_D, // Seven Segment Digit 1
25 output oHEX1_DP, // Seven Segment Digit 1 decimal point
26 output [6:0] oHEX2_D, // Seven Segment Digit 2
27 output oHEX2_DP, // Seven Segment Digit 2 decimal point
28 output [6:0] oHEX3_D, // Seven Segment Digit 3
29 output oHEX3_DP, // Seven Segment Digit 3 decimal point
30 output [6:0] oHEX4_D, // Seven Segment Digit 4
31 output oHEX4_DP, // Seven Segment Digit 4 decimal point
32 output [6:0] oHEX5_D, // Seven Segment Digit 5
33 output oHEX5_DP, // Seven Segment Digit 5 decimal point
34 output [6:0] oHEX6_D, // Seven Segment Digit 6
35 output oHEX6_DP, // Seven Segment Digit 6 decimal point
36 output [6:0] oHEX7_D, // Seven Segment Digit 7
37 output oHEX7_DP, // Seven Segment Digit 7 decimal point
38 //////////////////////////////// LED ////////////////////////////
39 output [8:0] oLEDG, // LED Green[8:0]
40 output [17:0] oLEDR, // LED Red[17:0]
41 //////////////////////////////// UART ////////////////////////////
42 output oUART_TXD, // UART Transmitter
43 input iUART_RXD, // UART Receiver
44 output oUART_CTS, // UART Clear To Send
45 input iUART_RTS, // UART Requst To Send
46 //////////////////////////////// IRDA ////////////////////////////
47 output oIRDA_TXD, // IRDA Transmitter
48 input iIRDA_RXD, // IRDA Receiver
49 //////////////////////////////// SDRAM Interface ////////////////////////
50 inout [31:0] DRAM_DQ, // SDRAM Data bus 32 Bits
51 output [12:0] oDRAM0_A, // SDRAM0 Address bus 12 Bits
52 output [12:0] oDRAM1_A, // SDRAM1 Address bus 12 Bits
53 output oDRAM0_LDQM0, // SDRAM0 Low-byte Data Mask
54 output oDRAM1_LDQM0, // SDRAM1 Low-byte Data Mask
55 output oDRAM0_UDQM1, // SDRAM0 High-byte Data Mask
56 output oDRAM1_UDQM1, // SDRAM1 High-byte Data Mask
57 output oDRAM0_WE_N, // SDRAM0 Write Enable
58 output oDRAM1_WE_N, // SDRAM1 Write Enable
59 output oDRAM0_CAS_N, // SDRAM0 Column Address Strobe
60 output oDRAM1_CAS_N, // SDRAM1 Column Address Strobe
61 output oDRAM0_RAS_N, // SDRAM0 Row Address Strobe
62 output oDRAM1_RAS_N, // SDRAM1 Row Address Strobe
63 output oDRAM0_CS_N, // SDRAM0 Chip Select
64 output oDRAM1_CS_N, // SDRAM1 Chip Select
65 output [1:0] oDRAM0_BA, // SDRAM0 Bank Address
66 output [1:0] oDRAM1_BA, // SDRAM1 Bank Address
67 output oDRAM0_CLK, // SDRAM0 Clock
68 output oDRAM1_CLK, // SDRAM0 Clock
69 output oDRAM0_CKE, // SDRAM0 Clock Enable
70 output oDRAM1_CKE, // SDRAM1 Clock Enable
71 //////////////////////////////// Flash Interface ////////////////////////
72 inout [14:0] FLASH_DQ, // FLASH Data bus 15 Bits (0 to 14)
73 inout FLASH_DQ15_AM1, // FLASH Data bus Bit 15 or Address A-1
74 output [25:0] oFLASH_A, // FLASH Address bus 26 Bits
75 output oFLASH_WE_N, // FLASH Write Enable
76 output oFLASH_RST_N, // FLASH Reset
77 output oFLASH_WP_N, // FLASH Write Protect /Programming Acceleration
78 input iFLASH_RY_N, // FLASH Ready/Busy output
79 output oFLASH_BYTE_N, // FLASH Byte/Word Mode Configuration
80 output oFLASH_OE_N, // FLASH Output Enable
81 output oFLASH_CE_N, // FLASH Chip Enable
82 //////////////////////////////// SRAM Interface ////////////////////////
83 inout [31:0] SRAM_DQ, // SRAM Data Bus 32 Bits
84 inout [3:0] SRAM_DPA, // SRAM Parity Data Bus
85 output [20:0] oSRAM_A, // SRAM Address bus 21 Bits
86 output oSRAM_ADSC_N, // RAM Controller Address Status
87 output oSRAM_ADSP_N, // SRAM Processor Address Status
88 output oSRAM_ADV_N, // SRAM Burst Address Advance
89 output [3:0] oSRAM_BE_N, // SRAM Byte Write Enable
90 output oSRAM_CE1_N, // SRAM Chip Enable
91 output oSRAM_CE2, // SRAM Chip Enable
92 output oSRAM_CE3_N, // SRAM Chip Enable
93 output oSRAM_CLK, // SRAM Clock
94 output oSRAM_GW_N, // SRAM Global Write Enable
95 output oSRAM_OE_N, // SRAM Output Enable
96 output oSRAM_WE_N, // SRAM Write Enable
97 //////////////////////////////// ISP1362 Interface ////////////////////////
98 inout [15:0] OTG_D, // ISP1362 Data bus 16 Bits
99 output [1:0] oOTG_A, // ISP1362 Address 2 Bits
100 output oOTG_CS_N, // ISP1362 Chip Select
101 output oOTG_OE_N, // ISP1362 Read
102 output oOTG_WE_N, // ISP1362 Write
103 output oOTG_RESET_N, // ISP1362 Reset
104 inout OTG_FSPEED, // USB Full Speed, 0 = Enable, Z = Disable
105 inout OTG_LSPEED, // USB Low Speed, 0 = Enable, Z = Disable
106 input iOTG_INT0, // ISP1362 Interrupt 0
107 input iOTG_INT1, // ISP1362 Interrupt 1
108 input iOTG_DREQ0, // ISP1362 DMA Request 0
109 input iOTG_DREQ1, // ISP1362 DMA Request 1
110 output oOTG_DACK0_N, // ISP1362 DMA Acknowledge 0
111 output oOTG_DACK1_N, // ISP1362 DMA Acknowledge 1
112 //////////////////////////////// LCD Module 16X2 ////////////////////////////
113 inout [7:0] LCD_D, // LCD Data bus 8 bits
114 output oLCD_ON, // LCD Power ON/OFF
115 output oLCD_BLON, // LCD Back Light ON/OFF
116 output oLCD_RW, // LCD Read/Write Select, 0 = Write, 1 = Read
117 output oLCD_EN, // LCD Enable
118 output oLCD_RS, // LCD Command/Data Select, 0 = Command, 1 = Data
119 //////////////////////////////// SD Card Interface ////////////////////////
120 inout SD_DAT, // SD Card Data
121 inout SD_DAT3, // SD Card Data 3
122 inout SD_CMD, // SD Card Command Signal
123 output oSD_CLK, // SD Card Clock
124 //////////////////////////////// I2C ////////////////////////////////
125 inout I2C_SDAT, // I2C Data
126 output oI2C_SCLK, // I2C Clock
127 //////////////////////////////// PS2 ////////////////////////////
128 inout PS2_KBDAT, // PS2 Keyboard Data
129 inout PS2_KBCLK, // PS2 Keyboard Clock
130 inout PS2_MSDAT, // PS2 Mouse Data
131 inout PS2_MSCLK, // PS2 Mouse Clock
132 //////////////////////////////// VGA ////////////////////////////
133 output oVGA_CLOCK, // VGA Clock
134 output oVGA_HS, // VGA H_SYNC
135 output oVGA_VS, // VGA V_SYNC
136 output oVGA_BLANK_N, // VGA BLANK
137 output oVGA_SYNC_N, // VGA SYNC
138 output [9:0] oVGA_R, // VGA Red[9:0]
139 output [9:0] oVGA_G, // VGA Green[9:0]
140 output [9:0] oVGA_B, // VGA Blue[9:0]
141 //////////////////////////////// Ethernet Interface ////////////////////////////
142 inout [15:0] ENET_D, // DM9000A DATA bus 16Bits
143 output oENET_CMD, // DM9000A Command/Data Select, 0 = Command, 1 = Data
144 output oENET_CS_N, // DM9000A Chip Select
145 output oENET_IOW_N, // DM9000A Write
146 output oENET_IOR_N, // DM9000A Read
147 output oENET_RESET_N, // DM9000A Reset
148 input iENET_INT, // DM9000A Interrupt
149 output oENET_CLK, // DM9000A Clock 25 MHz
150 //////////////////////////////// Audio CODEC ////////////////////////////
151 inout AUD_ADCLRCK, // Audio CODEC ADC LR Clock
152 input iAUD_ADCDAT, // Audio CODEC ADC Data
153 inout AUD_DACLRCK, // Audio CODEC DAC LR Clock
154 output oAUD_DACDAT, // Audio CODEC DAC Data
155 inout AUD_BCLK, // Audio CODEC Bit-Stream Clock
156 output oAUD_XCK, // Audio CODEC Chip Clock
157 //////////////////////////////// TV Devoder ////////////////////////////
158 input iTD1_CLK27, // TV Decoder1 Line_Lock Output Clock
159 input [7:0] iTD1_D, // TV Decoder1 Data bus 8 bits
160 input iTD1_HS, // TV Decoder1 H_SYNC
161 input iTD1_VS, // TV Decoder1 V_SYNC
162 output oTD1_RESET_N, // TV Decoder1 Reset
163 input iTD2_CLK27, // TV Decoder2 Line_Lock Output Clock
164 input [7:0] iTD2_D, // TV Decoder2 Data bus 8 bits
165 input iTD2_HS, // TV Decoder2 H_SYNC
166 input iTD2_VS, // TV Decoder2 V_SYNC
167 output oTD2_RESET_N, // TV Decoder2 Reset
168 //////////////////////////////// GPIO ////////////////////////////////
169 inout [31:0] GPIO_0, // GPIO Connection 0 I/O
170 input GPIO_CLKIN_N0, // GPIO Connection 0 Clock Input 0
171 input GPIO_CLKIN_P0, // GPIO Connection 0 Clock Input 1
172 output GPIO_CLKOUT_N0, // GPIO Connection 0 Clock Output 0
173 output GPIO_CLKOUT_P0, // GPIO Connection 0 Clock Output 1
174 inout [31:0] GPIO_1, // GPIO Connection 1 I/O
175 input GPIO_CLKIN_N1, // GPIO Connection 1 Clock Input 0
176 input GPIO_CLKIN_P1, // GPIO Connection 1 Clock Input 1
177 output GPIO_CLKOUT_N1, // GPIO Connection 1 Clock Output 0
178 output GPIO_CLKOUT_P1 // PIO Connection 1 Clock Output 1
179 );
180
181 // All inout port turn to tri-state
182 assign SD_DAT = 1'bz; // SD Card Data
183 assign GPIO_0 = 32'hzzzzzzzzz; // GPIO Connection 0 I/O
184 assign GPIO_1 = 32'hzzzzzzzzz; // GPIO Connection 1 I/O
185 assign AUD_ADCLRCK = 1'bz; // Audio CODEC ADC LR Clock
186
187 // Turn On TV Decoder
188 assign oTD1_RESET_N = 1'bz; // TV Decoder1 Reset
189 assign oTD2_RESET_N = 1'bz; // TV Decoder2 Reset
190
191 // Disable USB speed select
192 assign OTG_FSPEED = 1'bz; // USB Full Speed, 0 = Enable, Z = Disable
193 assign OTG_LSPEED = 1'bz; // USB Low Speed, 0 = Enable, Z = Disable
194
195 // 16*2 LCD Module
196 assign oLCD_ON = 1'b1; // LCD ON
197 assign oLCD_BLON = 1'b1; // LCD Back Light
198
199 // FLASH
200 wire FLASH_16BIT_IP_A0;
201 assign oFLASH_BYTE_N = 1'b1; // FLASH Byte/Word Mode Configuration
202 assign oFLASH_RST_N = 1'b1; // FLASH Reset
203 assign oFLASH_WP_N = 1'b1; // FLASH Write Protect /Programming Acceleration
204
205 // SSRAM
206 wire [1:0] SRAM_DUMMY_ADDR; // used to ignore the A0/A1 pin from Cypress SSRAM IP core
207 assign oSRAM_ADSP_N = 1'b1; // SRAM Processor Address Status
208 assign oSRAM_ADV_N = 1'b1; // SRAM Burst Address Advance
209 assign oSRAM_CE2 = ~oSRAM_CE1_N; // SRAM Chip Enable
210 assign oSRAM_CE3_N = oSRAM_CE1_N; // SRAM Chip Enable
211 assign oSRAM_GW_N = 1'b1; // SRAM Global Write Enable
212 assign oSRAM_CLK = cpu_clk; // SRAM Clock
213
214 // SDRAM
215 wire sdram_clk; // SDRAM Clock
216 wire [12:0] dram_a; // SDRAM Address bus 12 Bits
217 wire [1:0] dram_ba; // SDRAM Bank Address
218 wire dram_cas_n; // SDRAM Column Address Strobe
219 wire dram_cke; // SDRAM Clock Enable
220 wire dram_cs_n; // SDRAM Chip Select
221 wire [3:0] dram_dqm; // SDRAM Data Mask
222 wire dram_ras_n; // SDRAM Row Address Strobe
223 wire dram_we_n; // SDRAM Write Enable
224
225 // SDRAM0
226 assign oDRAM0_CLK = sdram_clk; // SDRAM0 Clock
227 assign oDRAM0_A = dram_a; // SDRAM0 Address bus 12 Bits
228 assign oDRAM0_BA = dram_ba; // SDRAM0 Bank Address
229 assign oDRAM0_CAS_N = dram_cas_n; // SDRAM0 Column Address Strobe
230 assign oDRAM0_CKE = dram_cke; // SDRAM0 Clock Enable
231 assign oDRAM0_CS_N = dram_cs_n; // SDRAM0 Chip Select
232 assign oDRAM0_LDQM0 = dram_dqm[0]; // SDRAM0 Low-byte Data Mask
233 assign oDRAM0_UDQM1 = dram_dqm[1]; // SDRAM0 High-byte Data Mask
234 assign oDRAM0_RAS_N = dram_ras_n; // SDRAM0 Row Address Strobe
235 assign oDRAM0_WE_N = dram_we_n; // SDRAM0 Write Enable
236
237 // SDRAM1
238 assign oDRAM1_CLK = sdram_clk; // SDRAM1 Clock
239 assign oDRAM1_A = dram_a; // SDRAM1 Address bus 12 Bits
240 assign oDRAM1_BA = dram_ba; // SDRAM1 Bank Address
241 assign oDRAM1_CAS_N = dram_cas_n; // SDRAM1 Column Address Strobe
242 assign oDRAM1_CKE = dram_cke; // SDRAM1 Clock Enable
243 assign oDRAM1_CS_N = dram_cs_n; // SDRAM1 Chip Select
244 assign oDRAM1_LDQM0 = dram_dqm[2]; // SDRAM1 Low-byte Data Mask
245 assign oDRAM1_UDQM1 = dram_dqm[3]; // SDRAM1 High-byte Data Mask
246 assign oDRAM1_RAS_N = dram_ras_n; // SDRAM1 Row Address Strobe
247 assign oDRAM1_WE_N = dram_we_n; // SDRAM1 Write Enable
248
249 // NIOS CPU
250 wire cpu_clk; // CPU Clock
251 wire cpu_reset_n; // CPU Reset
252
253 // Reset Delay for CPU
254 Reset_Delay delay0 (
255 .iRST(iKEY[0]), // Pushbutton[0]
256 .iCLK(iCLK_50), // 50 MHz
257 .oRESET(cpu_reset_n) // CPU Reset
258 );
259
260 // NIOS II system
261 nios_ii nios_ii0 (
262 // 1) global signals:
263 .clk_50(iCLK_50), // 50 MHz
264 .cpu_clk(cpu_clk), // CPU Clock
265 .sdram_clk(sdram_clk), // SDRAM Clock
266 .reset_n(cpu_reset_n), // CPU Reset
267 // the_lcd
268 .LCD_E_from_the_lcd(oLCD_EN), // LCD Enable
269 .LCD_RS_from_the_lcd(oLCD_RS), // LCD Command/Data Select, 0 = Command, 1 = Data
270 .LCD_RW_from_the_lcd(oLCD_RW), // LCD Read/Write Select, 0 = Write, 1 = Read
271 .LCD_data_to_and_from_the_lcd(LCD_D), // LCD Data bus 8 bits
272 // the_pio_key
273 .in_port_to_the_pio_key(iKEY), // Pushbutton[3:0]
274 // the_pio_ledg
275 .out_port_from_the_pio_ledg(oLEDG), // LED Green[8:0]
276 // the_pio_ledr
277 .out_port_from_the_pio_ledr(oLEDR), // LED Red[17:0]
278 // the_pio_sw
279 .in_port_to_the_pio_sw(iSW), // Toggle Switch[17:0]
280 // the_sdram
281 .zs_addr_from_the_sdram(dram_a), // SDRAM Address bus 12 Bits
282 .zs_ba_from_the_sdram(dram_ba), // SDRAM Bank Address
283 .zs_cas_n_from_the_sdram(dram_cas_n), // SDRAM Column Address Strobe
284 .zs_cke_from_the_sdram(dram_cke), // SDRAM Clock Enable
285 .zs_cs_n_from_the_sdram(dram_cs_n), // SDRAM Chip Select
286 .zs_dq_to_and_from_the_sdram(DRAM_DQ), // SDRAM Data bus 32 Bits
287 .zs_dqm_from_the_sdram(dram_dqm), // SDRAM Data Mask
288 .zs_ras_n_from_the_sdram(dram_ras_n), // SDRAM Row Address Strobe
289 .zs_we_n_from_the_sdram(dram_we_n), // SDRAM Write Enable
290 // the_trisate_bridge_flash_avalon_slave
291 .address_to_the_cfi_flash({oFLASH_A[21:0], FLASH_16BIT_IP_A0}), // FLASH Address bus 26 Bits
292 .data_to_and_from_the_cfi_flash({FLASH_DQ15_AM1, FLASH_DQ}), // FLASH Data bus 15 Bits (0 to 14)
293 .read_n_to_the_cfi_flash(oFLASH_OE_N), // FLASH Output Enable
294 .select_n_to_the_cfi_flash(oFLASH_CE_N), // FLASH Chip Enable
295 .write_n_to_the_cfi_flash(oFLASH_WE_N), // FLASH Write Enable
296 // the_tristate_bridge_ssram_avalon_slave
297 .address_to_the_ssram({oSRAM_A[17:0], SRAM_DUMMY_ADDR}), // SRAM Address bus 21 Bits
298 .adsc_n_to_the_ssram(oSRAM_ADSC_N), // RAM Controller Address Status
299 .bw_n_to_the_ssram(oSRAM_BE_N), // SRAM Byte Write Enable
300 .bwe_n_to_the_ssram(oSRAM_WE_N), // SRAM Write Enable
301 .chipenable1_n_to_the_ssram(oSRAM_CE1_N), // SRAM Chip Enable
302 .data_to_and_from_the_ssram(SRAM_DQ), // SRAM Data Bus 32 Bits
303 .outputenable_n_to_the_ssram(oSRAM_OE_N), // SRAM Output Enable
304 // the_uart
305 .cts_n_to_the_uart(oUART_CTS), // UART Clear To Send
306 .rts_n_from_the_uart(iUART_RTS), // UART Requst To Send
307 .rxd_to_the_uart(iUART_RXD), // UART Receiver
308 .txd_from_the_uart(oUART_TXD), // UART Transmitter
309 // the_seg7
310 .avs_s1_export_oHEX_from_the_seg7({oHEX7_DP, oHEX7_D, oHEX6_DP, oHEX6_D, oHEX5_DP,
311 oHEX5_D, oHEX4_DP,oHEX4_D, oHEX3_DP, oHEX3_D, oHEX2_DP, oHEX2_D, oHEX1_DP, oHEX1_D,
312 oHEX0_DP, oHEX0_D}) // 7-SEG Dispaly
313
314 );
315
316 endmodule

Step 10:
Quartus II編譯與Programmer燒入

Step 11:
使用Nios II EDS建立Hello World project

將hello_world.c改成以下程式

hello_world.c / C

1 /*
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : hello_world.c
5 Compiler : Nios II gcc
6 Description : Demo how to display 7 seg. on DE2-70
7 Release : 10/20/2008 1.0
8 */
9 #include <stdio.h>
10 #include "system.h"
11 #include "unistd.h" // usleep()
12 #include "SEG7.h" // SEG7_Hex()
13
14 int main() {
15 int i;
16
17 for(i = 0; i <= 100; i++) {
18 usleep(1 * 1000 * 1000);
19 SEG7_Decimal(i, 0x10);
20 }
21 } 

第10行

usleep(1 * 1000 * 1000);

usleep(1 * 1000 * 1000);

表示暫停1秒鐘,讓人眼看的到七段顯示器的變化。

11行

SEG7_Decimal(i, 0x10);

使用我們剛剛所寫的SET_Decimal() API,讓七段顯示器從0數到100。

執行結果

seg7_17

Question
(這是我當時給學生的homework,各位有興趣可以自己自做做看)

1.試著參考Avalon-MM Interface Specification ,自己將Slave Read補上,讓Nios II可以得知目前七段顯示器所顯示的數字。

2.請解釋SEG7_Hex()中,為什麼seg_mask = szMap[Data & 0x0F]?

3.將0到100在七段顯示器改用16進位顯示。

完整程式碼下載
lab2_files.7z(一個未完成的半成品,可以根著本文一步一步完成)
seg7_controller.7z (最後完整的結果)

Conclusion
為什麼挑七段顯示器當範例呢?因為本篇主要在講Avalon Bus Slave IP的開發,若使用其他週邊,又必須去了解開周邊的datasheet,恐會失焦於我想強調的Slave IP。可別小看Slave IP的好用喔,無論是自己寫Controller,或是自己寫一個硬體加速的IP,一定會留很多register給user設定,讓整個設計更加彈性,這些register都可以用Slave的方式,讓user使用Nios II的C語言去設定,讓軟硬體做整合,所以Slave應用非常廣,是學Nios II一定要會的課題,下一篇Blog主要是使用Terasic提供的DE2_70_SD_Card_Audio_Player範例做講解,沒啥原創性,但主要是要為第4個Lab做鋪路:一個從SD卡讀取照片的數位像框,在這篇我們將實際為LTM寫一個Controller for Avalon Bus (Altera與Terasic都沒提供),是使用Master IP,與本文的Slave IP不同,最後更可配合OS的多工特性,可以同時播放音樂,同時展示數位相框。

See Also
(原創) 如何自己用SOPC Builder建立一個能在DE2-70上跑μC/OS-II的Nios II系統? (SOC) (Quartus II) (SOPC Builder) (Nios II) (μC/OS-II) (DE2-70)
(原創) 如何設計一個七段顯示器Controller? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個SD卡Wav Player? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個數位相框? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)

posted on 2010-08-12 15:13  真 OO无双  阅读(20588)  评论(5编辑  收藏  举报

导航