Đánh giá chủ đề:
  • 82 Vote(s) - Trung bình 2.7
  • 1
  • 2
  • 3
  • 4
  • 5
Thiết kế MCU bằng VHDL
Bài viết: #1
1. Giới thiệu
Khi nhắc tới CPU, có lẽ điều đầu tiên nhiều người nghĩ đến là những thương hiệu quen thuộc như Intel Core 2 Duo, hay Core i5, Core i7… Và khi đọc tiêu đề của bài viết này, có thể bạn sẽ tự hỏi liệu chúng ta có thể thiết kế những CPU như vậy bằng VHDL, và nếu được thì liệu có FPGA nào chứa nổi chúng? Rõ ràng đó là điều không tưởng, và tất nhiên bài viết này không có ý định to tát đó. Ở đây, chúng ta chỉ đề cập CPU như là một máy Von Neuman đơn giản, có khả năng đọc lệnh, dữ liệu từ bộ nhớ, thực thi một vài lệnh cơ bản trên các dữ liệu đó, rồi lưu trữ trở lại. Liệu việc thiết kế một thứ đơn giản như vậy có mang lại lợi ích gì không? Trước hết, nó là một bài tập tổng hợp rất hữu ích để áp dụng các kiến thức đã học được (HDL, mạch tổ hợp, mạch có nhớ, FSM…). Hơn nữa, có đôi lúc hiệu quả sản phẩm lại nằm ở tính đơn giản và nhỏ gọn. Giả sử (giả sử thôi) ta cần xử lí tín hiệu âm thanh trên FPGA. Toàn bộ các khối xử lí DSP đã được viết bằng HDL. Để làm các việc còn lại như giao tiếp với thiết bị âm thanh (ADC, DAC, sound codec…), giao tiếp người dùng (button, switch…) việc dùng HDL là khá bất tiện so với các ngôn ngữ phần mềm thông dụng như C, ASM. Mặt khác, lại không quá phức tạp (và không đủ tài nguyên) để dùng đến các lõi processor chuyên nghiệp. Lúc đó, ta có thể lôi các thiết kế CPU nho nhỏ ra, tối ưu, lược bỏ lại một ít, và nhúng vào thiết kế. Tuy nhiên, đó là chuyện khá xa xôi và lạc đề, bây giờ chúng ta sẽ đề cập một số khái niệm cơ bản về máy Von Neuman.

Về cấu trúc tổng quát có thể chia CPU thành 2 phần:


- Datapath (DU): phần này làm nhiệm vụ thao tác dữ liệu. Đó là các thao tác số học, logic, giao tiếp bộ nhớ… Datapath bao gồm các khối hàm như cộng, trừ, so sánh (hợp thành ALU); thanh ghi và bộ nhớ để tạm thời lưu dữ liệu; bus và các bộ đa hợp (multiplexer), giải đa hợp (demultiplexer) nhằm chuyển vận data đúng lúc, đúng nơi giữa các phần của datapath. Datapath cũng xuất ra các status signal để Control Unit biết trạng thái hiện tại của hệ thống mà điều khiển phù hợp (ví dụ như giá trị lệnh vừa lấy được từ ROM, hay các cờ tràn, cờ âm, cờ zero…).

- Control unit (CU): Phần này đảm nhiệm tạo các control signal phù hợp vào thời điểm thích hợp để điều khiển Datapath hoạt động chính xác. Tại một thời điểm, CU ở vào một trạng thái nhất định, trạng thái này xác định bởi giá trị của bộ nhớ trạng thái (các FF). Các control signal được tạo ứng với mỗi trạng thái nhất định thông qua các mạch tổ hợp. Từ phân tích này ta thấy CU rất giống FSM. Thực vậy, các version đời đầu của lõi xử lí mềm PicoBlaze nổi tiếng của Xilinx đã từng được gọi là Ken Chapman Programmable State Machine (KCPSM), tức cũng là một dạng FSM, chỉ khác là hành vi của nó có thể thay đổi được dựa trên các giá trị đọc ra từ bộ nhớ (code và data).

Đại để là như thế này:
[Image: 1_control_datapath.jpg]Ở hình trên, 2 thanh ghi PC, IR và Memory sẽ tạo thành khối instruction_fetch. Nếu ko có nhảy hay ngắt quãng bất thường thì PC sẽ tăng dần tương ứng với từng lệnh trong code. Sau đó IR được load giá trị là ô nhớ trong memory có địa chỉ là giá trị của PC. Lúc này nội dung IR chính là mã lệnh cần thực thi. Quá trình này được điều khiển bởi 2 control_signal như trong hình (load PC, load IR) và tương ứng với state fetch trong FSM của control unit. Sau đó nội dung IR sẽ được đưa sang Control Unit để decode. Sau khi xác định yêu cầu của lệnh, FSM sẽ quyết định sẽ rẽ nhánh sang state nào tiếp theo. Tương ứng với mỗi nhánh, FSM sẽ đưa ra các control_signal thích hợp để điều khiển datapath (register, ALU, shifter…) để thực thi lệnh đúng đắn. Đồng thời, trong một số lệnh, datapath cần phản hồi các status_signal sang Control Unit để có quyết định thích hợp (chẳng hạn lệnh nhảy có điều kiện JNZ...). Điều này thể hiện sự tương quan “nhịp nhàng” giữa Control Unit và Datapath. Khi thiết kế, cần đảm bảo sự tách biệt giữa 2 phần này, đảm bảo 2 phần chỉ giao tiếp với nhau thông qua các signal, ko để các luồng data từ Datapath “lấn sân” sang Control Unit, bởi vì sự độc lập này là cơ sở để có thể dễ dàng tính toán Critical Path (nôm na là đường đi dài nhất của dữ liệu trong chuỗi tính toán, giữa các thanh ghi, giữa thanh ghi và bộ nhớ). Các thông số về Crirital Path rất cần thiết để tối ưu các thành phần của Datapath (ảnh hưởng trực tiếp tới tốc độ bộ xử lí) cũng như trong quá trình cài đặt kĩ thuật pipeline cho máy. Tuy vậy, các chủ đề trên khá phức tạp, vượt qua mục tiêu của bài viết này, phần tiếp theo sẽ tiến hành trên các máy Von Neuman đơn giản.


2. Phân tích một CPU đơn giản
Đáng lẽ thì phải giới thiệu các bước trong quá trình thiết kế trước, sau đó mới đi vào xem xét chi tiết CPU bằng HDL. Nhưng có lẽ để dễ hình dung thì tốt hơn là nên tìm hiểu một máy Von Neuman rất đơn giản trước. Máy này chỉ sử dụng ROM để lưu trữ chương trình, ko sử dụng RAM.

Trước tiên là sơ đồ Datapath, phần xử lí dữ liệu (xem hình dưới).

Các thành phần Register, ALU, Multiplexer đều là các khối cơ bản quen thuộc. Khối ALU thực hiện các phép toán trên 2 toán hạng là 2 thanh ghi A và B (theo bảng các phép toán bên dưới). Kết quả phép toán có thể được update vào thanh ghi B. Thanh ghi A có thể load giá trị từ thanh ghi B sang hoặc đọc input từ bên ngoài tùy thuộc vào lựa chọn của bộ Multiplexer. Đồng thời cũng có thêm cổng output để có thể xuất giá trị thanh ghi A ra ngoài. Các đường mảnh hơn là các control signal để điều khiển các thành phần trên: Amul (chọn input cho thanh ghi A từ input bên ngoài hoặc từ thanh ghi B), ALoad (cập nhật nội dung A), AClr (xóa A, thường là khi reset), BLoad, BClr, ALUins (chọn lệnh cho ALU). Từ thanh ghi B, một bộ so sánh sẽ đưa ra status_signal là B_neq_0 để xác định giá trị B có bằng 0 hay ko nhằm phục vụ lệnh nhảy có điều kiện.
[Image: 2_datapath_block.jpg]Bảng lệnh của ALU
Instruction Operator
000 Output ← A
001 Output ← A + B
010 Output ← A - B
011 Output ← A + 1
100 Output ← A - 1
101 Output ← A and B
110 Output ← A or B
111 Output ← A xor B
Tiếp tục, sơ đồ hình dưới mô tả khối instruction_fetch:
[Image: 3_instruction_fetch_block.jpg] Ở đây sử dụng thanh ghi PC 4bit để trỏ tới vị trí chương trình hiện tại. Giá trị PC được dùng làm địa chỉ cho ROM. Giá trị lệnh đọc từ ROM ra được đưa vào IR. Giá trị PC có thể được cập nhật tăng lên 1, hoặc đước load từ một phần của IR (trường hợp lệnh nhảy). Ngoài các control_signal IRLoad, IRClr, PCLoad, PCClr tương tự như trên, còn có PCmul dùng để chọn đầu vào cho PC. Nội dung IR được đưa sang Control Unit để decode mã lệnh. Tập lệnh của máy này đơn giản như sau:

[Image: 2ebd8ea9a95651623d62ceba281030ff09f221a7...905e6g.jpg]



Để thực hiện được các lệnh trên, Datapath cần được điều khiển theo FSM hình dưới.

Như đã đề cập ở phần nguyên tắc chung ở trên, sau khi reset, FSM sẽ lặp lại chuỗi thao tác fetch, decode, execute. Do đây là máy Von Neuman rất đơn giản nên có nhiều thao tác đã được lược bỏ, chẳng hạn như operan_fetch, memory_access… Ở mỗi trạng thái trong FSM, cần đưa ra được các control_signal đúng đắn cho Datapath để thực hiện yêu cầu. Bảng dưới đưa ra giá trị tương ứng


[Image: 6_FSM.jpg][Image: 7_Control_signals.jpg]Sau khi kết nối 2 phần Datapath và Control Unit lại cũng như gắn thêm các cồng IO, ta được sơ đồ hoàn chỉnh như sau:
[Image: 8_CPU.jpg]
Các chi tiết về cài đặt FSM cũng như Datapath có thể tham khảo trong các giáo trình thông dụng về VHDL hoặc Verilog, có thể xem project đính kèm bài này để biết thêm chi tiết (Quartus 9.0, VHDL).

Sau khi hiện thực toàn bộ thiết kế bằng VHDL, ta tiến hành tổng hợp và mô phỏng. Mỗi lần thay đổi firmware, cần chỉnh sửa các giá trị trong file program.mif, sau đó trong Quartus chọn Processing/Update Memory…để cập nhật giá trị vào ROM, cuối cùng tiến hành mô phỏng để quan sát hoạt động của CPU.

Dưới đây là một đoạn “chương trình” nhỏ, làm nhiệm vụ nhập giá trị từ input, sau đó xuất ra các giá trị giảm dần đến 0:

DEPTH=16;
WIDTH=8;
ADDRESS_RADIX = BIN;
DATA_RADIX = BIN;

-- address : data
CONTENT
BEGIN
[0000..1111] : 00000000; -- Initialize
--
0000 : 00000000; -- A <- input_port
0001 : 01110000; -- B <- A - 1
0010 : 00010000; -- A <- B
0011 : 00110000; -- output <- A
0100 : 10010001; -- JNZ 0001 (lặp lại cho đến khi B = 0)
0101 : 10000101; -- JMP 0101 (nhảy để dừng tại chỗ)
END;

Và đây là mô phỏng tương ứng:
Ngõ vào clk được cấp xung chu kì 30ns, ngõ rst mức thấp (nếu mô phỏng với modelsim thì phải cho rst mức cao trong 1 chu kì clk để reset các thanh ghi), input được giữ cố định giá trị 3, output lần lượt được xuất các giá trị 2, 1, 0 đúng theo yêu cầu.
Do kiểu mã hóa trạng thái trong FSM là one-hot nên trên waveform ta có thể quan sát các state riêng rẽ. Trước tiên, khi khởi động sẽ là state s_RESET, sau đó lần lượt fetch, decode và các state execute tương ứng với mỗi lệnh. Giá trị PC tăng đều đặn với các lệnh thông thường, nhảy về 1 khi gặp lệnh nhảy JNZ, giữ nguyên giá trị 5 khi gặp lệnh JMP. Thanh ghi IR được load giá trị từ ROM tương ứng với địa chỉ là giá trị của PC. Giá trị thanh ghi A và B thay đổi tương ứng từng dòng mã lệnh.
[Image: 9_waveform.jpg]
Lưu ý là thiết kế này chỉ mô phỏng được thôi, chứ nạp xuống DE2 thì không được. Lí do là vì ta dùng khối lpm_rom cho ROM, khối này là megafunction của Altera, thể hiện asynchronous ROM, tức là không cần cấp clk mà chỉ cần cấp địa chỉ là có output. Khối này chỉ được hỗ trợ trong dòng device APEX20KE, còn CycloneII trong kit DE2 thì không. Do đó, thiết kế ở phần sau phải sử dụng khối khác.


3. Quá trình thiết kế CPU
Như vậy, sau khi tìm hiểu cách thức hoạt động và phối hợp giữa các thành phần trong CPU, ta đã phần nào hình dung được về quá trình thiết kế. Tổng quát thì có các bước như sau:
1) Thiết kế kiến trúc lệnh, liệt kê tập lệnh cần thiết, đây là việc rất quan trọng. Cần tính toán kỹ lưỡng số lượng lệnh, các chức năng cần có, các chế độ định địa chỉ hỗ trợ… Nếu làm thương mại thì phải làm sao cho tập lệnh gọn nhất để tốn ít tài nguyên hơn, nhưng phải đảm bảo đáp ứng đủ nhu cầu khách hàng và dự phòng sau này. Nếu làm “chơi vui” hoặc làm kiểu “đồ án” thì có thể tham khảo tập lệnh của các VDK, VXL khác rồi lai tạo tùy ý. Chi tiết bước này sẽ được trình bày sau.

2) Việc tiếp theo là xây dựng một datapath để đáp ứng các lệnh đã thiết kế. Đây là một công việc lâu dài nhưng có thể làm từ từ. Nghĩa là có thể làm cho nó “chạy được” trước đã, còn việc tối ưu hóa có thể để sau. Việc tối ưu hóa là rất cần thiết vì nó quyết định phần lớn đến độ lớn của tần số clk tối đa mà CPU làm việc (một cách tương đối là tần số càng cao thì tốc độ CPU càng nhanh). Datapath cần có trước tiên là một ALU để làm tính. Các môi trường làm việc như Altera Max+Plus II hay Xilinx ISE đều tích hợp sẵn các core cho phép toán như + - * /. Nếu có nhu cầu tối ưu hóa thì sẽ sử dụng các khối riêng. Tiếp đó cần có nơi lưu dữ liệu. Mới đầu thì ta dùng vài thanh ghi, sau đó upgrade lên tập hợp thanh ghi (Registers File) và RAM. Nếu hỗ trợ nhiều kiểu định địa chỉ toán hạng cho CPU thì cần có các mux hoặc hệ thống bus với các tristate để xác định source đưa vào ALU đúng lúc.

Nói chung 2 bước đầu tiên này cần làm xoay vòng liên tục cho đến khi đạt yêu cầu.

3) Như đã nói, các component trên của datapath đều được điều khiển bởi các control signal (ví dụ như thanh ghi có các tín hiệu Load, Clear; ALU thì có ins để biết phải làm phép tính nào…). Sau khi làm xong datapath thì gom nhóm các control signal lại và đặt các tên có ý nghĩa để dễ nhớ khi làm Control Unit. Sau đó lập bảng ghi giá trị các control signal ứng với từng lệnh cụ thể.

4) Xây dựng Control Unit dùng phương pháp FSM.

5) Kết hợp CU và DP lại tạo nên CPU hoàn chỉnh.

6) Test bằng …mã máy. Đơn giản là vì không ai viết compiler cho con CPU tự chế của chúng ta.

Sau đây là sơ lược các bước nhỏ của việc thiết kế tập lệnh (ISA: instruction set architecture), chi tiết tham khảo cuốn DSP with FPGA, UweMeyer-Baese, 3rd, phần 9.

a/ Xác định các addressing modes

Addressing mode mô tả cách xác định các toán hạng trong lệnh. Các uP với cấu trúc CISC hỗ trợ nhiều kiểu định địa chỉ, còn RISC thì giới hạn ở một số kiểu thường dùng nhất.

b/ Xác định kiến trúc data-flow, nôm na là cấu trúc lệnh bao gồm bao nhiêu operand. Thường gồm các kiểu Zero-, One-, Two-, Three-Address. Chẳng hạn 8051 có lệnh gồm 2 operand (vd: ANL data,#data), hoặc kiến trúc stack-machine không dùng toán hạng nào vì mặc định ngầm hiểu là mọi phép toán thực hiện trên 2 phần tử top của stack.

c/ Xác định operand-source, có thể là từ register, memory, external…

Thường gồm 3 kiểu, Von Neuman (cả instruction và data cùng chung 1 memory), Havard (tách instruction và data ra 2 memory riêng), Couple-port (có 2 port ra cho memory chứa data, nhằm giảm thời gian operand-fetch). Các cấu trúc hardware cụ thể là RAM và RegisterFile.

d/ Các dạng thức lệnh cần có. Thường 1 uP cần hỗ trợ các lệnh ALU (số học, logic, dịch), chuyển dữ liệu (move, load…), điều khiển (jmp, goto…)

e/ Tương quan giữa lệnh hiện tại đang thực thi và lệnh kế tiếp. Hầu hết các lệnh kế tiếp đều nằm liền sau lệnh hiện tại trong instruction memory, ngoại trừ các lệnh nhảy. Tuy nhiên, cũng có một số kiến trúc sử dụng thêm toán hạng trong mã lệnh để trỏ tới địa chỉ tiếp theo trong tất cả các lệnh.

Lý thuyết chung là vậy, bây giờ ta sẽ bắt tay vào làm một uP cụ thể, hướng theo một tập lệnh khá phổ biến, của họ 8051, tất nhiên là không thể đầy đủ ngay từ đầu được. Chi tiết tập lệnh này có thể tìm dễ dàng trong các tài liệu cơ bản về vi điều khiển.

8051 có data-flow dạng two-operand, kiến trúc Havard (instruction memory, RAM, GPR, SFR), trước mắt ta chỉ sử dụng GPR (16 thanh ghi đa dụng) và SFR (16 thanh ghi + ACC +PSW). GPR và SFR hay ACC, PSW cũng là các thanh ghi như nhau, tuy nhiên GPR chỉ sử dụng trong tính toán ở CPU, SFR thì cần được truy cập đồng thời bởi CPU và các ngoại vi (timer chẳng hạn), còn ACC và PSW là các thanh ghi đặc biệt mà hầu hết kiến trúc đều có. Do chỉ có 16 thanh ghi cho mỗi loại nên ta có thể sử dụng 4 bit để mã hóa địa chỉ, và do đó chỉ cần 1 word cho mỗi lệnh. Sau này khi đưa RAM vào thì sẽ có thêm một số yêu cầu mới. Thứ nhất là quá trình đánh địa chỉ các thanh ghi. Nếu làm theo kiểu 8051, tức là ánh xạ các thanh ghi vào không gian nhớ, thì sẽ thuận tiện trong tính toán, tuy nhiên tốn kém bit để mã hóa. Còn nếu để các thanh ghi riêng như một số kiến trúc RISC thì sẽ tiết kiệm, nhưng tập lệnh lại không linh hoạt. Thứ hai là cách thức truy cập RAM. Có thể theo kiểu load-store, như RISC điển hình, tức là chỉ truy cập bộ nhớ bằng lệnh load, store, sau khi đã lấy được giá trị từ bộ nhớ vào thanh ghi thì các lệnh khác chỉ thực hiện trên thanh ghi. Hoặc như 8051, có cả lệnh làm tính trực tiếp giữa giá trị trong RAM và giá trị thanh ghi. Thứ ba là cách thức sắp xếp địa chỉ trong lệnh. Nếu hỗ trợ RAM càng lớn thì số bit mã hóa không gian địa chỉ càng nhiều. Do đó nảy sinh 2 lựa chọn, để từ lệnh dài, nhưng mỗi lệnh chỉ 1 từ, hoặc tách từ lệnh ra thành nhiều phần liền kề nhau trong instruction memory. Việc lựa chọn này ảnh hưởng khá lớn đến quá trình xây dựng Control Unit cũng như pipeline sau này. Nhưng đó là chuyện sau này, còn bây giờ tạm thời ta vẫn chưa có RAM [Image: 1.gif]

8051 hỗ trợ tới 6 kiểu định địa chỉ (addressing mode), một đặc trưng của CISC (thực ra mình cũng không rõ 8051 là CISC hay là RISC nữa), để có nhiều kiểu địa chỉ như vậy cần các phối hợp phức tạp giữa các thành phần của datapath. Bước đầu ta sẽ giản lược bớt, chỉ sử dụng kiểu địa chỉ ngầm định, thanh ghi và tức thời. Sử dụng 4 bits cho GPS, 4 bits cho SFR (Register addressing), các thanh ghi ACC và PSW sẽ được mã hóa vào lệnh (Implied addressing), ngoài ra có thêm kiểu tức thời (Immediate addressing), tức là operand lấy từ ngay mã lệnh.

Ta cũng sử dụng các lệnh ALU như của 8051 và giản lược bớt: add, addc, sub, subb, and, or, xor. Lệnh chuyển dữ liệu duy nhất là MOV (giữa GPS, SFR, ACC, PSW). Do không gian địa chỉ chưa lớn nên ta chỉ sử dụng các lệnh điều khiển đơn giản (CALL-RET, JMP, JC, JZ).

Như vậy sau khi “thanh lọc nội bộ”, ta có tập lệnh gồm 29 lệnh (xem file đính kèm), khá đơn giản, nhưng vậy là đủ cho bước khởi đầu. Cấu trúc mã hóa từ lệnh trong IR như sau:
[Image: hlab2_IR.jpg] Tiếp theo là quá trình thiết kế bus. Phù hợp nhất cho kiểu two-operand là kiến trúc three-bus, 2 bus nguồn sẽ lấy data từ các thành phần GPR, SFR, Immediate đề đưa vào 2 nguồn của ALU, 1 bus đích để đưa giá trị tính toán được của ALU trở lại các thành phần. Việc phân xử bus sẽ do các tristate đảm nhận. Các lệnh chuyển dữ liệu sẽ không đi qua ALU mà sử dụng 2 tristate để đi từ bus nguồn sang bus đích và cập nhật vào toán hạng đích. Nếu không dùng 2 khóa này thì có thể làm thêm cho ALU một lệnh bypass để chuyển data. Tuy nhiên, theo tham khảo thì tốt nhất là nên cho ALU tập trung vào việc của nó, tức là tính toán, còn việc khác thì cứ đưa ra ngoài, lí do là tiết kiệm năng lượng và dễ quản lí (cái này mình cũng không rõ lắm). Khi ALU tính toán sẽ sinh ra các cờ tràn, cờ zero…, các cờ này được cập nhật vào thanh ghi PSW trực tiếp, không qua busD. Giá trị thanh ghi PSW sẽ được đưa sang ControlUnit để phục vụ các lệnh nhảy có điều kiện cần phải xem xét giá trị các cờ. Khối instruction-fetch lúc này vẫn còn nằm khá độc lập, chỉ bao gồm ROM, PC và IR. Một phần IR đưa sang ControlUnit để giải mã lệnh, một phần tách thành ImmediateOperand đưa ra bus, một phần tách thành địa chỉ để chọn GPRs và SFRs trong các lệnh. Sơ bộ như hình dưới:[Image: hlab2_bus_arch.jpg] Tuy nhiên, có 1 điểm khá bất tiện, đó là CycloneII không hỗ trợ internal-tristate. Nôm na là tristate chỉ dùng để điều khiển các chân top-level ở ngoài, còn trong bus thì không, synthesis-tool sẽ chuyển thành các mạch OR. Do vậy ta chuyển sang sử dụng Multiplexer để phân xử bus. Dùng các bộ MUX này có vẻ “thắt cổ chai” hơn là tristate, tuy nhiên, khi chạy trên FPGA của Altera thì các mux có vẻ được hỗ trợ tốt. Đại khái dùng Mux là như hình dưới: [Image: hlab2_mux_arch.jpg]
Sau khi đã xong datapath, ta chi tiết hóa các control signal của từng thành phần, gom nhóm lại các signal liên quan, sau đó lập bảng giá trị tương ứng cho từng lệnh, chi tiết xem file đính kèm.

Sau khi đã có bảng control signal, việc thiết kế Control Unit bằng FSM cũng khá dễ dàng. Trong project này, ta không gán tương ứng mỗi lệnh một trạng thái như trước, mà tách ra 2 trạng thái lớn (s_NORMAL và s_BRANCH), trong mỗi trạng thái này lại xét tiếp mã lệnh để đưa ra control signal phù hợp.

Nếu các bạn để ý kĩ thì có thể thấy rằng với datapath như trên, ta chưa thể thực hiện được lệnh RET, đó là vì chưa có chỗ để lưu giá trị PC trước khi nhảy trong lệnh CALL. Một số các kiến trúc lưu giá trị này vào một stack riêng, khi thực thi CALL thì push PC vào stack, khi thực thi RET thì pop ra từ stack và cập nhật lại PC. 8051 cũng dùng stack, nhưng chứa trong RAM. Một số kiến trúc khác lại sử dụng các tập thanh ghi đặc biệt, gọi là shadow-register, dùng để bảo lưu giá trị tập thanh ghi, không chỉ trong lệnh CALL, mà còn khi xảy ra interrupt hoặc trap, gọi chung là context-switching.

Một điểm khác biệt so với thiết kế trước đó là khối ROM, ta không thể sử dụng asynchronous trong Cyclone II, do đó phải sử dụng MegaWizard trong Quartus tạo khối altsyncram để chứa mã lệnh chương trình. Khi mô phỏng ta phải cấp cho đường ROM_clk tần số gấp đối đường clk chính (hoặc có thể làm thêm khối PLL). Ngoài ra, các hằng số cũng được đưa riêng ra một file để tiện quản lí.

Sau đó kết nối 2 phần Datapath và Control Unit lại để tạo CPU hoàn chỉnh. Ở đây ta đưa các SFR(0-3) ra để làm port output.
[Image: hlab2_CPU.jpg]Sau khi tổng hợp thành công, ta viết thử một đoạn mã máy để test, chức năng đoạn mã này tương tự như trước, tức là xuất ra các giá trị giảm dần trên một port output, ở đây là SFR0.

CONTENT BEGIN
000000000 : 01000000000111; -- mov A, #7
000000001 : 00010100000001; -- sub A, #1
000000010 : 01010000000000; -- mov SFR0, A
000000011 : 01101100000011; -- JZ #3 ;nhảy tại chỗ nếu bằng 0
000000100 : 01101000000001; -- JMP #1 ;tiếp tục giảm nếu chưa bằng 0
[000000101..011111111] : 00000000000000;
END;

Kết quả mô phỏng ra chính xác.
Đính kèm bài viết là các project (Altera QuartusII 9.0) của 2 thiết kế:
You are not allowed to view links. Register or Login to view.
You are not allowed to view links. Register or Login to view.
You are not allowed to view links. Register or Login to view.
You are not allowed to view links. Register or Login to view.





Nguồn You are not allowed to view links. Register or Login to view.
Trả lời


Đi tới chuyên mục:


Thành viên đang xem chủ đề: 1 Khách