7.1 SƠ ĐỒ KHỐI
Hình 7.1: Sơ đồ của khối timer trì hỗn ban đầu
Tên Mơ tả
ICLK Xung clock 50Mhz từ kit DE2
VS Tín hiệu VS (Vertical Sync) từ ADV7181B HS Tín hiệu HS (Horizontal Sync) từ ADV7181B TD_Stable Báo hiệu ADV7181b đã hoạt đợng ổn định RST0, RST1, RST3 Ngõ ra cho phép các khối khác bắt đầu làm việc 7.2 MƠ TẢ
Với cấu hình đã cài đặt ở phần trước, khi đã hoạt đợng ổn định, dạng sĩng do ADV7181B phát ra như sau:
Hình 7.2: Mơ tả dạng sĩng ADV7181B
Vì vậy để phát hiện xem chip mã hĩa này đã hoạt đợng ổn định hay chưa khối TD_DETEC tiến hành kiểm tra điều kiện: VS ở mức cao trong 9 chu kỳ liên tiếp của HS rồi chuyển xuống mức thấp, nếu thỏa mãn thì đưa TD_Stable lên mức cao. Khi tín hiệu TD_Stable lên mức cao, khối RESET_DELAY bắt đầu đếm lên theo xung nhịp của ICLK (50MHz) để tính thời điểm xuất ra mức 1 trên các chân RST0, RST1, RST2. Các tín hiệu này dùng để khởi đợng các khối khác theo trình tự như sau:
a) Ban đầu xĩa tất cả dữ liệu trong các khối.
b) Tính từ thời điểm TD_Stable lên 1 (đơn vị là chu kỳ clock 27MHz) - Sau 1132461.5: tích cực RST0 để kích hoạt khối SDRAM BUFFER.
- Sau 1698692.5: tích cực RST1 để kích hoạt khối Desize Horizon.
- Sau 2264923.5: tích cực RST2 để kích hoạt khối xử lý ảnh YUV và VGA controller. c) Giữ nguyên giá trị ngõ ra cho đến khi cĩ tín hiệu RESET hệ thống thì lặp lại.
Điểm cần chú ý ở đây là khi Desize Horizon hoạt đợng thì sẽ xuất DATA_VALID cho phép ghi dữ liệu vào SDRAM BUFFER. Rồi phải chờ mợt khoảng thời gian để ghi đủ số liệu cần thiết mới kích hoạt VGA Controller để xuất dữ liệu từ SDRAM BUFFER. Như ta đã biết mợt frame ảnh do ADV7181B xuất ra bao gồm 900900 byte (525 line, mỡi line cĩ 1716 byte) hay để truyền hết mợt frame sẽ mất 900900 chu kỳ. Do xung clock trên chân LLC để truyền các byte là 27MHz nên ta kiểm tra lại các thời điểm này như sau:
- Lấy gốc thời gian là khi bắt đầu frame đầu tiên.
- TD_Stable lên 1 khi Frame đầu tiên đã phát được 9 line: 9 x 1716 = 15444 chu kỳ. - Frame thứ ba được bắt đầu tại thời điểm 2 x 900900 = 1801800
- Khối Desize Horizon được kích hoạt tại thời điểm 1714136.5
(= 15444 + 1698692.5) tức là trước khi frame thứ ba bắt đầu. Đảm bảo rằng khối sẽ xuất ra DATA_VALID = 1 ở tồn bợ các Active Pixel của frame thứ 3.
- Khối VGA Controller được kích hoạt tại thời điểm 2280367.5(=15444 + 2264923.5) nên oRequest được xuất ra tại thời điểm 2315727.5(= 2280376.5 + 35360). Với 35360 chu kỳ là khoảng thời gian từ khi khối được reset cho đến khi oRequest lên 1. Vậy việc đọc từ SDRAM BUFFER được kích hoạt khi frame thứ 3 đã bắt đầu được mợt khoảng thời gian là 513927.5(= 2315727.5 – 1801800).
Điều này đảm bảo cho việc xuất ra đúng từng frame từ SDRAM BUFFER mà ta sẽ đề cập kỹ hơn ở phần mơ tả SDRAM BUFFER.
8. KHỐI DISIZE_HORIZON
8.1 SƠ ĐỒ KHỐI
Hình 8.1: Sơ đồ khối Disize_Horizon
Tên Mơ tả
CLK_27 Xung clock 27MHz từ kit DE2
RST_N Reset hệ thống
TD_DATA[7:0] Dữ liệu hình ảnh từ ADV7181B
ACLR Tín hiệu xĩa bất đồng bợ do khối Timer trì hỗn cung cấp CLK Xung clock 27MHz từ chân TD_CLK của ADV7181B Số chia = 9 Số chia cung cấp cho bợ chia do người thiết kế nhập vào
TV_X[9:0] Vị trí của Pixel trong hàng hiện hành đồng thời cũng là số bị chia cung cấp cho bợ chia
Thương[9:0] Thương của phép chia TV_X cho 9 Số dư [9:0] Số dư của phép chia TV_X cho 9
DATA_VALID Đồng bợ cho oYCbCr để đưa vào SDRAM_Controller oYCbCr[15:0] Chuỡi dữ liệu ảnh ngõ ra
DATA_VALID: ở mức 1 thì sẽ cho phép Pixel đi kèm được ghi vào SDRAM thơng qua SDRAM_Controller. Do frame mà ADV7181B xuất ra cĩ dạng 720 x 480 để đưa về chuẩn 640 x 480 mà hình ảnh khơng bị xén thì với mỡi 9 pixel liên tiếp ta sẽ loại bỏ Pixel đầu tiên: Khơng cho phép ghi vào SDRAM bằng cách đưa DATA_VALID xuống mức 0 (lấy ra 8 Pixel trong 9 Pixel: 640 = x 720 ).
Đồng thời để đảm bảo được chuỡi đưa vào SDRAM_Controller vẫn cĩ dạng chuỡi CbYnCrYn+1 liên tiếp thì phải hốn đổi giữa 2 thành phần Cb và Cr cứ sau 2 lần loại bỏ 1 Pixel.
Hình 8.2: Vị trí các Pixel trong chuỗi
Như ở hình trên X là vị trí các Pixel bị loại bỏ (bị bỏ qua khi hiển thị lên màn hình), khi đĩ chuỗi Pixel tại S1 là Cb4Y8Cb5Y10 và tại E1 là Cr8Y17Cr9Y19 vì vậy để đảm bảo chuỗi cĩ dạng CbYCrY liên tiếp thì phải hốn đổi vị trí giữa Cb và Cr trong khoảng Cb5Y10…Cr8Y17.
8.2 MƠ TẢ
TD_DATA là chuỡi Pixel được phát ra theo chuẩn Video ITU656. Ta cĩ thể xem mợt frame thực sự bắt đầu với Odd Field khi bit F (bit 6 trong byte cuối của trường SAV hay EAV) chuyển từ 1 về 0, vậy để xét điều kiện bắt đầu của mợt frame ta phải đợi đến trường SAV hay EAV rồi mới kiểm tra giá trị của bit F:
Window <= {Window[15:0],iTD_DATA}; if (Window == 24’hFF0000)
//khi phát hiện trường SAV (EAV) thì gán giá trị bit V cho FVAL và bit F cho //Field
begin
Field <= iTD_DATA[6]; end
//kiểm tra điều kiện bit F chuyển từ 1 về 0 để bắt đầu 1 frame như sau:
Pre_Field <= Field;
if ({ Pre_Field, Field } == 2’b10) Start <= 1’b1;
//khởi động bộ đếm cont để xác định số byte của chuỡi Pixel trong 1 hàng
if (SAV) begin cont <= 18’h0; Active_video <= 1’b0; End else if (cont < 1440) cont <= cont+1’b1;
//cứ 2 byte 1 Pixel khi xác định vị trí Pixel trong hàng thì phải chia cont cho 2
assign oTV_X = cont>>1;
Để thực hiện phép chia oTV_X cho 9 ta sử dụng bợ chia từ thư viện của quatus:
Phần Menu >> Tools >> MegaWizard Plug_in Manager…>>Create… tạo custom mới đặt tên là DVI; chọn phần Arithmetic >> LPM_DEVIDE. Vì oTV_X ≤ 720 nên chọn đợ rợng bit của số bị chia (Numberator) là 10, đợ rợng bit của số chia (denominator) là 4, kiểu dữ liệu khơng dấu. Vì số chia cần nhập là 9 nên ta ghép vào khối tổng thể như sau:
DIV u5 ( .aclr(!DLY0), .clock(TD_CLK), .denom(4’h9), .number(TV_X), .quotient(Quotient), .remain(Remain));
Trong đĩ quotient, remain là thương và số dư, ta nhập các điều kiện oTV_X cĩ chia hết cho 9 và thương là số lẻ thơng qua các chân iSkip và iSwap_CbCr bằng cách khai báo:
Desize_Horizontal u4 ( .iTD_DATA(TD_DATA), .oTV_X(TV_X), .oYCbCr(YCbCr), .oDVAL(TV_DVAL), .iSwap_CbCr(Quotient[0]), .iSkip(Remain==4'h0), .iRST_N(DLY1), .iCLK_27(TD_CLK) );
Sau đĩ ghép 1 Y với 1 Cr hay 1 Y với 1 Cb đồng thời hốn đổi vị trí của Cr và Cb tại các vị trí cần thiết:
if(iSwap_CbCr) begin
0: Cb <= iTD_DATA; 1: YCbCr <= {iTD_DATA,Cr}; 2: Cr <= iTD_DATA; 3: YCbCr <= {iTD_DATA,Cb}; endcase end else begin
case(Cont[1:0]) //khơng cần hoán đổi
0: Cb <= iTD_DATA; 1: YCbCr <= {iTD_DATA,Cb}; 2: Cr <= iTD_DATA; 3: YCbCr <= {iTD_DATA,Cr}; endcase end
Sau đĩ xét thêm điều kiện Cont[0] để đảm bảo việc ghép 1 byte Y với 1 byte Cr hay 1 byte Y với 1 byte Cb đã hồn thành để xuất DATA_VALID :
if(Start && FVAL && Active_Video && Cont[0] && !iSkip ) Data_Valid <= 1'b1;
else
Data_Valid <= 1'b0;
Như vậy Data_Valid chỉ lên 1 ở Active Pixel để điều khiển sự ghi vào SDRAM BUFFER.
9. KHỐI SDRAM BUFFER
9.1 SƠ ĐỒ KHỐI
Hình 9.1: Sơ đồ khối SDRAM BUFFER
Tên Mơ tả
RESET Tín hiệu reset hệ thống CLK_27 Xung clock 27MHz từ kit DE2 CLK
Xung clock 81MHz PLL đưa ra cho các ngõ vào CLK của khối SDRAM Controller ( chính là tần số đọc của
SDRAM READ FIFO2)
SDR_CLK Xuất xung clock 81MHz cho SDRAM
WR_LOAD RD1_LOAD RD2_LOAD
Lần lượt là tín hiệu để xĩa bất đồng bợ SDRAM WRITE FIFO, SDRAM READ FIFO1 và SDRAM READ FIFO2 lấy từ chân RST0 của khối Timer trì hỗn ban đầu.
WR_DATA Dữ liệu ảnh đưa vào SDRAM WRITE FIFO do Desize horizon cấp
WR Cho phép ghi vào SDRAM WRITE FIFO lấy từ chân DATA_VALID của khối Desize horizon
WR_CLK Xung clock 27MHz từ chân LLC(TD_CLK) của ADV7181B
RD_WRFIFO Cho phép đọc dữ liệu từ SDRAM WRITE FIFO
WRITE_SIDE[8:0] Số từ (Word) hiện cĩ trong SDRAM WRITE FIFO
DATA_IN Dữ liệu từ SDRAM WRITE FIFO đưa vào Control Center để ghi SDRAM.
DATA_OUT[15:0]
Dữ liệu Control Center đọc từ SDRAM để xuất ra ngồi qua 1 trong 2 FIFO: SDRAM READ FIFO1, SDRAM READ FIFO2
RD1 RD2
RD1 = ~ RD2: Lần lượt cho phép đọc dữ liệu từ SDRAM READ FIFO1, SDRAM READ FIFO2 với sự điều khiển của khối VGA Cotroller thơng qua chân Request và VGA_Y.
RD1_CLK RD2_CLK
Tần số đọc của SDRAM READ FIFO1 và SDRAM READ FIFO2 được là 27MHz từ KIT DE2
READ_SIDE1[8:0] Số từ (Word) hiện cĩ trong SDRAM READ FIFO1 READ_SIDE2[8:0] Số từ (Word) hiện cĩ trong SDRAM READ FIFO2 WR_RDFIFO1 Cho phép ghi dữ liệu SDRAM READ FIFO1 WR_RDFIFO2 Cho phép ghi dữ liệu SDRAM READ FIFO2 RD1_DATA[15:0]
RD2_DATA[15:0] Dữ liệu ngõ ra cung cấp cho khối xử lý ảnh YUV
Các chân DQ[15:0], SA[11:0], CKE, CAS_N, RAS_N, SDR_CLK, WE_N, BA[1:0], CS_N[1:0], DQM[1:0] thì được nối tương ứng vào chip SDRAM cĩ sẵn trên kit DE2.
9.2 MƠ TẢ
Như ta đã biết 1 frame ảnh theo chuẩn ITU656 bao gồm Odd Field và Even Field: khi xuất ra màn hình thì các line thuợc Odd Field sẽ được hiển thị ở hàng lẻ, cịn các line thuợc Even là hàng chẵn. Nên các line của 2 Field này phải được xuất xen kẽ nhau nhưng trong chuỡi video ITU656 do ADV7181B xuất ra thì 2 Field được xuất liên tục: xuất xong Odd Field rồi mới tới Even Field (các frame khi ghi vào SDRAM thì thành 2 Field liên tục) nên để xuất ra các line xen kẽ thì ta phải tuần tự xuất 1 line từ địa chỉ mà Odd Field được lưu giữ rồi lại xuất tiếp 1 line từ địa chỉ mà Even Field được lưu giữ.
Dữ liệu trong mợt frame ảnh sẽ được ghi lần lượt vào SDRAM từ địa chỉ 0 đến địa chỉ 324480 (324480 = 640 x 507, 507 chính là số line của frame được ghi vào SDRAM ,ta bỏ qua 18 line cĩ bit V =1 ), lúc này phần dữ liệu cần xuất ra từ SDRAM chia thành 2 phần (trong 1 frame theo chuẩn ITU656 thực sự cĩ tới 487 active line, ta xén bớt 7 active line để giảm số line về chuẩn hiển thị là 480):
• Phần 1: Từ địa chỉ 8320 (640 x 13) đến 161920 (640 x 253) sẽ là các Pixel thuợc Odd Field. Đây chính là 240 line từ 23 đến 262 trong frame gốc.
• Phần 2: Từ địa chỉ 170880 (640 x 267) đến 324480 (640 x 507) là các Pixel thuợc Even Field. Đây chính là 240 line từ 286 đến 525 trong frame gốc.
SDRAM hỡ trợ chế đợ truy cập dữ liệu theo từng khối (Burst) với chiều dài khối cĩ thể thay đổi được nhờ vào cài đặt giá trị 3 bit cuối (BL) của thanh ghi mode register bằng cách truy cập chế đợ load mode rồi nhập giá trị cho thanh ghi này qua các chân địa chỉ:
Ở đây đọc và ghi theo từng khối 128 word 16 bit nên nhập BL = 111: chiều dài của Burts là full page (tức là 256 word với việc sử dụng SDRAM dưới dạng 4M x 16); WT=0: truy xuất tuần tự (Sequential) dữ liệu trong khối; LTMODE = 011: thời gian chờ (latency) cho tín hiệu RAS là 3 chu kỳ;
Các Burst dữ liệu của 2 phần trên sẽ được xuất xen kẽ nhau. Ta khởi tạo và chi xuất địa chỉ cho các phần này như sau:
if(!RESET_N) begin rWR_ADDR <= 0; rWR_MAX_ADDR <= 640*507; rRD1_ADDR <= 640*13; rRD1_MAX_ADDR <= 640*253; rRD2_ADDR <= 640*267; rRD2_MAX_ADDR <= 640*507;
//chiều dài của khối cần truy xuất
rWR_LENGTH <= 128; rRD1_LENGTH <= 128; rRD2_LENGTH <= 128; end else begin
//nếu đã thực hiện xong tác vụ mWR_DONE, mRD_DONE và cĩ cờ báo thực hiện tác vụ mới đối với một khối WR_MASK[0], RD_MASK[0], RD_MASK[1] thì tăng địa chỉ khối lên 1 khối và lặp lại cho đến khi vượt quá địa chỉ tối đa thì quay về địa chỉ ban đầu.
if(WR_LOAD) begin rWR_ADDR <= WR1_ADDR; rWR_LENGTH <= WR1_LENGTH; end else if(mWR_DONE&WR_MASK[0]) begin if(rWR_ADDR<rWR_MAX_ADDR-rWR_LENGTH) rWR_ADDR <= rWR_ADDR+rWR_LENGTH; else rWR_ADDR <= WR_ADDR; end
//đọc dữ liệu từ phần 1
if(RD1_LOAD) begin rRD1_ADDR <= RD1_ADDR; rRD1_LENGTH <= RD1_LENGTH; end else if(mRD_DONE&RD_MASK[0]) begin if(rRD1_ADDR<rRD1_MAX_ADDR-rRD1_LENGTH) rRD1_ADDR <= rRD1_ADDR+rRD1_LENGTH; else rRD1_ADDR <= RD1_ADDR; end
//đọc dữ liệu từ phần 2
if(RD2_LOAD) begin rRD2_ADDR <= RD2_ADDR; rRD2_LENGTH <= RD2_LENGTH; end else if(mRD_DONE&RD_MASK[1])
begin if(rRD2_ADDR<rRD2_MAX_ADDR-rRD2_LENGTH) rRD2_ADDR <= rRD2_ADDR+rRD2_LENGTH; else rRD2_ADDR <= RD2_ADDR; end end
Trước hết cần tạo mợt khối điều khiển việc ghi và đọc SDRAM xen kẽ nhau, mỡi lần đọc hay ghi dữ liệu sẽ thao tác trên từng Burst cĩ chiều dài là 128 từ (Word) theo thứ tự ưu tiên (chờ thao tác hiện thời hồn thành rồi mới thực hiện thao tác tiếp theo):
- Đọc 1 khối từ SDRAM rồi ghi vào SDRAM READ FIFO1 để xuất chuỡi Pixel thuợc Odd Frame. - Đọc 1 khối từ SDRAM rồi ghi vào SDRAM READ FIFO2 để xuất chuỡi Pixel thuợc Even Frame. - Ghi 1 khối từ SDRAM WRITE FIFO vào SDRAM.
Ở trên ta thực hiện 3 thao tác xen kẽ nhau, vì vậy để dữ liệu cĩ thể đồng bợ nhập, xuất dữ liệu với các khối khác thì phải cung cấp tần số làm việc cho SDRAM và tần số truy xuất dữ liệu giữa các khối FIFO và SDRAM gấp 3 lần tần số clock của các khối khác. Để tạo các xung clock này ta sử dụng thư viện của Quartus để tạo khối PLL :
Phần Menu >> Tools >> MegaWizard Plug_in Manager… >> Create… tạo mợt custom mới, đặt tên là SDRAM_PLL, chọn phần I/O >> ALTCLKILOCK, ta khơng sử dụng các chân đồng bợ mà chỉ nhập các thơng số cho tần số ngõ vào và tần số ngõ ra như sau: inclk0 là 27MHz; c0 chọn tần số là 81MHz với pha ban đầu là 0; c1 tần số là 81 MHz với pha ban đầu trễ 3ns (bù trừ với khảng thời gian điều khiển các tín hiệu đồng bợ để truy cập SDRAM).
Chân c0 sẽ cung cấp tần số đọc tần số cho SDRAM WRITE FIFO để ghi dữ liệu vào SDRAM, tần số ghi cho SDRAM READ FIFO1 và SDRAM READ FIFO2 để ghi dữ liệu được xuất ra từ SDRAM. Chân c1 cung cấp tần số làm việc cho SDRAM.
Đồng thời khi thực hiện 1 tác vụ ta cần phải trì hỗn các tác vụ khác mợt khoảng thời gian được mơ tả theo giãn đồ sau (chưa xét tác đợng của RD1 và RD2):
Hình 9.2: Giản đồ định thì cho chu kỳ truy xuất giữa SDRAM và các FIFO
Vì vậy để đảm bảo truy xuất đúng dữ liệu thì cần phải cĩ các FIFO cĩ chiều dài 384 ( tức là 128 x 3 ). Tuy nhiên trong thư viện của Quarus chỉ cĩ FIFO dài 384 Word nên sẽ tạo mợt FIFO dài 512 Word như sau:
Phần Menu >> Tools >> MegaWizard Plug_in Manager… >> Create … tạo mợt custom mới, đặt tên là SDRAM_WRITE_FIFO, chọn phần Memory Compiler >> FIFO chọn đợ rợng dữ liệu là 16bit, chiều dài ( deep ) là 512 Words. Làm tương tự để tạo các khối SDRAM_READ_FIFO1 và SDRAM_READ_FIFO2.
Khi sử dụng FIFO dài 512 Word ta phải cĩ 1 số thay đổi trong thiết kế, tuy nhiên các thay đổi này tương đối đơn giản như tăng tần số xung clock lên 108 MHz, sử dụng thêm 1 tác vụ ghi trống (WR2) để đảm bảo dữ liệu xuất ra đung theo yêu cầu.
Thực hiện ghi và xuất từng khối dữ liệu xen kẽ từ SDRAM như sau:
//ghi vào SDRAM READ FIFO1 các Pixel thuộc line Odd frame
if( (READ_SIDE1< rRD1_LENGTH) ) begin mADDR <= rRD1_ADDR; mLENGTH <= rRD1_LENGTH; WR_MASK <= 2'b00; RD_MASK <= 2'b01; mWR <= 0; mRD <= 1; end
//ghi vào SDRAM READ FIFO2 các Pixel thuộc line Even frame
begin mADDR <= rRD2_ADDR; mLENGTH <= rRD2_LENGTH; WR_MASK <= 2'b00; RD_MASK <= 2'b10; mWR <= 0; mRD <= 1; end
//đọc dữ liệu từ SDRAM WRITE FIFO và ghi vào SDRAM
else if( (WRITE_SIDE>= rWR_LENGTH)&& (rWR_LENGTH!=0) ) begin mADDR <= rWR_ADDR; mLENGTH <= rWR_LENGTH; WR_MASK <= 2'b01; RD_MASK <= 2'b00; mWR <= 1; mRD <= 0; end end if(mWR_DONE) begin WR_MASK <= 0; mWR <= 0; end if(mRD_DONE) begin RD_MASK <= 0; mRD <= 0; end
Xét điều kiện số Word cĩ trong các FIFO để khởi tạo lệnh đọc và ghi SDRAM. Rồi dùng biến đếm ST (bắt đầu từ 0) để thiết lập khoảng thời gian cần thiết cho 1 tác vụ bao gồm: thời gian chờ bus đảm bảo rảnh hồn (đối với lệnh đọc là SC_CL+SC_RCD+1, ghi là SC_CL-1, phụ thuợc
vào cấu trúc của SDRAM: SC_CL = SC_RCD = 3 được khai báo trong tập tin Sdram_Params.h ), thời gian thực hiện tác vụ (mLENGTH = 128).Tạo tín hiệu điều khiển việc ghi đọc các FIFO và cờ báo đã đọc hay ghi xong như sau:
if(Read) begin
//OUT_VALID là tín hiệu dùng để điều khiển cho phép ghi vào các SDRAM //READ FIFO
if(ST==SC_CL+SC_RCD+1) OUT_VALID <= 1; else if(ST==SC_CL+SC_RCD+mLENGTH+1) begin