• Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

  • Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

  • Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Integrated Circuit Design - Chia sẻ kiến thức về vi mạch

    Vi mạch và Ứng dụng

Thứ Bảy, 9 tháng 9, 2017

[Basic Knowledge][Bài 1] Hướng dẫn phân tích thiết kế lõi IP step-by-step - Phân tích tổng quan

Bạn đang muốn bắt tay vào thiết kế một lõi IP (IP core) nhưng không biết bắt đầu từ đâu? Bài viết này là một phần trong chuỗi bài viết hướng dẫn bạn đi từng bước để có thể thiết kế được một lõi IP hoàn chỉnh. Phương pháp được sử dụng ở đây là top-down, phân tích từ tổng quan đến chi tiết. Ví dụ được trình bày là một thiết kế CPU cơ bản (simple CPU).

1. Tổng quan
Phân tích thiết kế (thiết kế ở đây là lõi IP) là một công đoạn biến một "yêu cầu thiết kế" thành RTL code hoàn chỉnh. Công đoạn này gồm các bước cơ bản sau đây:
  1. Nghiên cứu và tìm hiểu
  2. Phân tích tổng quan
  3. Phân tích chi tiết
  4. Mô tả RTL code
  5. Kiểm tra RTL code

Hình 1. Các bước phân tích thiết kế một lõi IP
Để minh họa cụ thể việc làm thế nào để thiết kế được 1 lõi IP. Chúng ta sẽ thực hiện yêu cầu thiết kế sau:

Thiết kế một lõi CPU 8 bit, gọi là SCPU, hỗ trợ tập lệnh sau (tham khảo ví dụ ở trang http://minnie.tuhs.org):
Hình 2. Tập lệnh của SCPU

2. Nghiên cứu và tìm hiểu
2.1 Hướng dẫn

Nghiên cứu và tìm hiểu là thực hiện tìm kiếm và phân tích những lý thuyết, nguyên lý và các khái niệm liên quan đến yêu cầu thiết kế với mục tiêu nắm rõ các vấn đề.

Vấn đề cần nghiên cứu và tìm hiểu được thực hiện tùy vào mức độ hiểu biết của người thực hiện thiết kế. Nó có thể là nguyên lý hoạt động, các chuẩn giao tiếp, các giao thức, các cấu trúc, ứng dụng của thiết kế, ...

Hình thức nghiên cứu: Với sự phát triển của công nghệ thông tin như hiện nay, việc tìm kiếm thông tin trở nên đơn giản hơn rất nhiều. Ngoài sách, bài báo khoa học, báo cáo khoa học, các sản phẩm ứng dụng thực tiễn, kinh nghiệm và hiểu biết của người khác, ... thì internet là một công cụ đắc lực. Điểm quan trọng của việc nghiên cứu lúc này không phải là tìm kiếm thông tin mà là đánh giá và phân tích thông tin có được.

Mục tiêu: hiểu rõ những lý thuyết liên quan đến thiết kế để phân tích, đánh giá, so sánh và lựa chọn hướng thiết kế.

Cách thức thực hiện: Phân chia yêu cầu thiết kế thành những vấn đề từ tổng quan đến chi tiết để tìm hiểu. Mỗi vấn đề là một câu hỏi mà người thiết kế chưa có câu trả lời hoặc không chắc chắn về câu trả lời.
Hình 2. Phân chia yêu cầu thiết kế thành các vấn đề cần nghiên cứu ở nhiều cấp độ
Khi nhận được một yêu cầu thiết kế, người thiết kế có thể không thể phân chia được đầy đủ những vấn đề rõ ràng ngay từ đầu mà chỉ có thể liệ kê được một vài vấn đề cơ bản để tìm hiểu dựa trên các từ khóa (keyword) có trong yêu cầu. Trong quá trình tìm hiểu các vấn đề cơ bản này, chúng sẽ được từ từ mở rộng thêm và các vấn đề khác sẽ được đặt ra để tiếp tục tìm hiểu.

2.2 Thực hiện ví dụ CPU 8 bit

Giả sử rằng bạn là người mới bắt đầu, yêu cầu thiết kế đã cho bạn những từ khóa qua trọng là CPU, 8 bit và tập lệnh. Hãy bắt đầu tìm hiểu từ những từ khóa này, mỗi câu hỏi và thắc mắc cảu bạn là một vấn đề cần phải tìm hiểu.
Hình 3. Liệt kê vấn đề dựa trên yêu cầu đề bài
Sau khi có khái niệm cơ bản, bạn sẽ tiếp tục gặp phải những thắc mắc tiếp theo, những câu hỏi lại xuất hiện và những vấn đề tiếp tục được liệt kê để tìm hiểu tiếp. Bạn sẽ lặp lại bước này nhiều lần cho đến khi nhận thấy đã đủ lượng kiến thức để hình dung cách thức hiện yêu cầu thiết kế. Trong quá trình thực hiện thiết kế, bạn có thể gặp tiếp những vấn đề khác cần giải quyết thì hãy quay lại bước này, liệt kê thêm vấn đề để tìm hiểu.
Hình 3. Mở rộng vấn đề cần tìm hiểu
Lưu ý, việc liệt kê và phân chia đề mục các vấn đề cần tìm hiểu là khác nhau đối với mỗi người thiết kế. Khi tìm hiểu, các vấn đề nên được thống kê chi tiết lại sẽ rất hữu ích khi bạn cần tham khảo lại hoặc chuyển giao cho người khác tiếp tục công việc hoặc khi bạn phát hiện có sai sót khi hiểu một vấn đề và cần xem xét lại.
Hình 4. Thống kê lại những thông tin đã tìm hiểu
Sau khi tìm hiểu, một CPU có cấu trúc cơ bản gồm những thành phần sau (lưu ý, bài viết chỉ trình bày ngắn gọn những thành phần cơ bản và cần thiết để phục vụ minh họa cho những phần sau):
  • Hoạt động cơ bản của một CPU gồm các bước sau:
    • Lấy lệnh (Fetch) từ bộ nhớ lưu mã lệnh (instruction memory), còn gọi là bộ nhớ chương trình (program memory). Từng mã lệnh (Instruction code) mà CPU phải thực thi được lưu sẵn trong bộ nhớ chương trình. Các mã lệnh là chuỗi binary được biên dịch từ code C/C++ hoặc assembly code.
    • Giải mã lệnh (Decode) dựa trên mã binary của từng lệnh CPU sẽ giải mã xem lệnh đó là lệnh gì và tích cực các tín hiệu điều khiển tương ứng
    • Thực thi lệnh (Execute) là thực hiện các tính toán và xử lý tương ứng với mã lệnh đã được giả mã như cộng, trừ, nhân, chia, dịch, ...
    • Truy xuất bộ nhớ (Memory Access) đọc/ghi bộ nhớ
    • Lưu kết quả (Write Back) là thực hiện lưu lại các kết quả sau quá trình Execute đến vị trí mà lệnh yêu cầu, ví dụ như các thanh ghi
Hình 5. Minh họa hoạt động cơ bản của một CPU
  • Các thành phần cơ bản của CPU
    • Bộ đêm chương trình PC (Program Counter) tạo giá trị địa chỉ truy cập bộ nhớ chương trình để lấy lệnh
    • Bộ nhớ chương trình (Program memory) lưu các lệnh mà CPU sẽ thực thi
    • Thanh ghi lưu mã lệnh IR (Instruction Register) lưu mã binary lấy từ bộ nhớ lệnh
    • Bộ giải mã lệnh Decoder
    • Các thanh ghi dùng để lưu giá trị trước và sau khi CPU thực thi tính toán (execute)
    • Bộ tính toán số học ALU (Arithmetic Logic Unit) thực thi các phép tính cộng, trừ, nhân, chia, dịch, ....
    • Bộ nhớ dữ liệu (Data memory) lưu các dữ liệu được sử dụng bởi các lệnh truy xuất bộ nhớ
  • Cấu trúc lệnh cơ bản
    • RISC (Reduced Instruction Set Computing) có độ dài mã lệnh cố định
    • CISC (Complex Instruction Set Computing) có độ dài mã lệnh có thể khác nhau giữa các lệnh
Lưu ý, đối với bài viết này, chúng ta chỉ thực hiện các thành phần cơ bản nhất từ đó các bạn có thể hiểu:
  1. Làm thế nào để thiết kế một lõi IP - mục đích chính
  2. Một thiết kế CPU cơ bản - mục đích phụ

3. Phân tích tổng quan
3.1 Hướng dẫn
Phân tích tổng quan là thực hiện xây dựng đặc điểm, cấu trúc đáp ứng yêu cầu thiết kế.

Phương thức thực hiện là lựa chọn những lý thuyết đã "nghiên cứu và tìm hiểu" có thể áp dụng được đối với từng yêu cầu thiết kế.

Mục tiêu là hình dung được tổng quan toàn bộ thiết kế trước khi phân tích chi tiết từng mạch nguyên lý.

Kết quả của bước này là:
  1. Đặc điểm và chức năng chính (features & function) mà thiết kế sẽ hỗ trợ
  2. Sơ đồ khối (Block diagram) Thể hiện mối liên hệ giữa các thành phần chức năng chính
  3. Sơ đồ tín hiệu giao tiếp (Signal/Pin/Port diagram) và mô tả tín hiệu: thể hiện các tín hiệu ngõ vào, ngõ ra. Các tín hiệu  được mô tả rõ chức năng, độ rộng, chiều, mức tích cực và lưu ý khác nếu có.
  4. Thanh ghi (Register table) thể hiện thông tin thanh ghi cấu hình trạng thái mà người sử dụng thiết kế có thể truy cập hoặc sử dụng.
  5. Sơ đồ định thời (Timing diagram) thể hiện sự ràng buộc và liên hệ giữa các tín hiệu giao tiếp. Sơ đồ này thường phải có đối với các giao thức yêu cầu một chuỗi bắt tay nhiều bước để thực hiện một tác vụ.
  6. Các yêu cầu khác: yêu cầu về công suất, tốc độ, tần số hoạt động, ...
Sau khi hoàn thành, những kết quả trên đây thường được đưa vào datasheet, user guide hoặc user manual của một chip hoặc lõi IP. Ví dụ, lõi "IP CAN v5.0, LogiCORE IP Product Guide, August 2, 2016" của Xilinx thể hiện các thành phần trên như sau:
  • Đặc điểm chức năng

  • Sơ đồ khối
  • Sơ đồ tín hiệu giao tiếp và mô tả
  • Mô tả thanh ghi
Một phần bảng mô tả thanh ghi

Mô tả chi tiết một thanh ghi với vị trí bit, tên, thuộc tính Read/Write/, giá trị mặc định và chức năng
  • Sơ đồ định thời
Minh họa một sơ đồ định thời

3.2 Thực hiện ví dụ CPU 8 bit (gọi tắt là SCPU)
Đây là một yêu cầu mở, không ràng buộc chi tiết đến từng đặc điểm cụ thể, chỉ yêu cầu một CPU xử lý dữ liệu 8 bit và hỗ trợ 16 lệnh như trong danh sách. Sẽ có rất nhiều cách để thực hiện được yêu cầu này và sau đây là một cách.

Phân tích tập lệnh của SPCU
CPU là một thiết kế đặc biệt, trong đó cấu trúc tập lệnh sẽ có ảnh hưởng lớn đến cấu trúc phần cứng sẽ thực hiện nên từng lệnh của CPU cần được phân tích cụ thể trước khi phân tích chi tiết hơn. Cấu trúc mã lệnh được lựa chọn ở đây là:

  • 16 bit
  • Độ dài cố định (kiểu RISC)
Hình 6. Phân tích cấu trúc các lệnh của SCPU (màu xám là trường không sử dụng)

Đặc điểm và chức năng

  1. Chức năng theo yêu cầu thiết kế
    1. Hỗ trợ 16 lệnh
    2. Bus dữ liệu 8 bit
  2. Chức năng được lựa chọn để thiết kế
    1. Kiến trúc RISC với độ dài mã lệnh là 16 bit
    2. Bộ nhớ chương trình và bộ nhớ dữ liệu dùng chung có dung lượng 256 byte
    3. Bộ tính toán số học ALU hỗ trợ ADD, SUB, AND và OR
    4. Hỗ trợ 4 thanh ghi tính toán R0, R1, R2 và R3 ứng với trường Rd và Rs chỉ có 2 bit

**Chú ý: Vì mỗi ô nhớ là 8 bit nên các lệnh có trường IMM sẽ được lưu trong 2 ô nhớ liên tiếp. 1 ô lưu 8 bit MSB và ô lưu giá trị IMM.

Sơ đồ khối
Từ những phân tích ở bước "nghiên cứu và tìm hiểu" và đặc điểm hỗ trợ, một sơ đồ khối cho ví dụ SCPU được thể hiện như sau:

  • Khối Fetch
    • Khối Memory là bộ nhớ chương trình và cũng là bộ nhớ dữ liệu 
    • Khối IR là thanh ghi 8 bit lưu mã lệnh lấy từ memory
    • Khối DR là thanh ghi 8 bit lưu dữ liệu từ memory. Thanh ghi này sẽ lưu giá trị IMM của các lệnh nhảy, lệnh LW và SW (số thứ tự từ 9 đến 16 trong bảng mã lệnh)
    • Khối PC: Tính toán giá trị PC truy xuất bộ nhớ chương trình lấy đầu vào từ:
      • Ngõ ra Decoder để lấy địa chỉ truy xuất đối với lệnh đọc bộ nhớ LW và ghi bộ nhớ SW
      • Thanh ghi DR để lấy giá trị IMM cho các lệnh nhảy
  • Khối Decoder giải mã lệnh tạo tín hiệu điều khiển các khối khác và cấp dữ liệu ghi vào Memory đối với lệnh SW. Đây là khối sẽ chứa các thanh ghi R0, R1, R2 và R3
  • Khối Execute chứa ALU thực thi tính toán số học và tạo giá trị cập nhật các thanh ghi tính toán R0, R1, R2 và R3
Hình 7. Sơ đồ khối SCPU
Sơ đồ tín hiệu giao tiếp và mô tả
Đối với thiết CPU này, chúng ta chỉ có 2 tín hiệu giao tiếp là:
  • Xung clock đồng bộ để SCPU chạy liên tục. Xung clock sẽ tích cực cạnh lên.
  • Tín hiệu reset để khởi động SCPU. Tín hiệu reset tích cực mức thấp

Mô tả thanh ghi
Thiết kế có các thanh ghi mà người dùng có thể tác động khi lập trình gồm:

  • Thanh ghi chương trình PC:
    • Chức năng: Chứa địa chỉ truy xuất bộ nhớ chương trình. Người sử dụng có thể tác động bởi lệnh nhảy
    • Độ rộng: 8 bit
  • Thanh ghi tính toán R0, R1, R2, R3:
    • Chức năng: Chứa giá trị dùng để tính toán và kết quả tính toán. Người sử dụng có thể tác động bởi các lệnh có trường hoặc vùng Rd hoặc Rs.
    • Độ rộng: 8 bit
Chúng ta đã kết thúc 2 bước trong quy trình phân tích thiết kế gồm "Nghiên cứu và tìm hiểu" và "Phân tích tổng quan".

**Mọi ý kiến góp ý và đóng góp, các bạn có thể email hoặc comment dưới bài viết để cùng chia sẻ và phát triển.

Thứ Tư, 6 tháng 9, 2017

[IP core] FIFO và LIFO đồng bộ - phiên bản hỗ trợ một số trường hợp đặc biệt

Bài này trình bày về một số trường hợp đặc biệt của FIFO và LIFO (STACK) đồng bộ và cách thực hiện mạch xử lý các trường hợp riêng này. Với FIFO, các trường hợp đặc biệt là khả năng ghi thêm khi FIFO đã đầy (full), khả năng đọc khi FIFO đang rỗng (empty). Với LIFO (STACK),  khả năng đọc và ghi cùng một thời điểm là vấn đề sẽ được đề cập đến.
Hai bài viết them khảo cần đọc trước khi đọc và hiểu bài viết này:

1. FIFO đồng bộ

1.1 Phân tích
Đầu tiên các bạn hãy tham khảo bài FIFO đồng bộ phiên bản 1 trong wesite này vì bài viết này sẽ chỉ trình bày những điểm khác biệt và các mạch mới sẽ được thêm vào dựa trên thiết kế có sẵn này.

Trong phiên bản này, gọi là phiên bản 1, khi FIFO đã đầy, tín hiệu sfifo_full=1 thì FIFO hoàn toàn không thể ghi tiếp. Nếu tiếp tục ghi khi FIFO đang đầy, wr=1, thì trạng thái tiếp theo sẽ là overflow, sfifo_ov=1. Khi FIFO đã rỗng, sfifo_empty=1, thì FIFO hoàn toàn không thể đọc được một giá trị dữ liệu hợp lệ nào. Nếu tiếp tục đọc, rd=1, khi FIFO đang rỗng thì trạng thái tiếp theo là underflow, sfifo_ud=1.

Phiên bản 1 có thể được nâng cấp với một vài điều chỉnh nhỏ để hỗ trợ thêm:
  1. Ghi khi FIFO đã đầy, sfifo_full=1
  2. Đọc khi FIFO đã rỗng, sfifo_empty=1

Hai điều trên có thể thực hiện được trong một trường hợp đặc biệt khi cả tín hiệu đọc (rd) và tín hiệu ghi (wr) cùng tích cực khi FIFO đang đầy hoặc đang rỗng. Cụ thể:
  1. Khi FIFO đầy (sfifo_full=1), nếu wr=rd=1 thì dữ liệu cần đọc sẽ được lấy ra khỏi FIFO và dữ liệu cần ghi sẽ được ghi vào FIFO ở cùng 1 cạch xung clock. Lúc này, cả hai con trỏ đọc và ghi cùng tăng. Trạng thái FIFO đầy vẫn được duy trì vì FIFO được lấy ra 1 dữ liệu nhưng đồng thời cũng nạp thêm 1 dữ liệu mới.
  2. Khi FIFO rỗng (sfifo_empty=1), nếu wr=rd=1 thì dữ liệu cần đọc sẽ được truyền trực tiếp từ ngõ vào FIFO đến ngõ ra FIFO mà không cần lưu vào bộ nhớ trung gian mem_array. Trạng thái FIFO rỗng vẫn được duy trì vì FIFO không lưu giá trị dữ liệu mới. Lúc này, cả hai con trỏ đọc và ghi không thay đổi giá trị.

Hình 1. Minh họa hai trường hợp đặc biệt của FIFO khi FULL và EMPTY
Một số điều chỉnh được thực hiện như sau:

Mạch tạo tín hiệu ghi vào FIFO, đồng thời là tín hiệu tăng con trỏ ghi:
  • Mạch này thêm trường hợp cho phép ghi khi FIFO đang FULL nhưng đang được đọc (sfifo_full AND rd)
  • Mạch này loại bỏ trường hợp ghi khi FIFO đang EMPTY nhưng đang được đọc (sfifo_empty AND rd NOR sfifo_full) vì lúc này dữ liệu đầu vào FIFO được chuyển trực tiếp đến đầu ra FIFO

Hình 2. Mạch tạo tín hiệu cho phép ghi vào FIFO
Mạch dữ liệu đầu ra FIFO được thêm phần xử lý chuyển tiếp dữ liệu khi FIFO đang EMPTY:
Hình 3. Mạch tạo dữ liệu đầu ra của FIFO
Mạch set/clear của tín hiệu báo overflow (sfifo_ov) và underflow (sfifo_ud):
  • Set khi không xuất hiện đọc/ghi cùng thời điểm
  • Clear dùng trực tiếp tín hiệu rd, đối với sfifo_ov, và wr đối với sfifo_ud

Hình 4. Mạch tạo set/clear tín hiệu báo overflow và underflow

Mạch tạo tín hiệu báo hợp lệ cho ngõ ra dữ liệu: Để hỗ trợ tốt cho việc phát hiện đúng giá trị dữ liệu hợp lệ được đọc ra từ FIFO. Thiết kế có thể thêm một tín hiệu báo hợp lệ, sfifo_valid đi kèm với dữ liệu data_out. Tín hiệu này chỉ tích cực nếu ngõ ra data_out là dữ liệu hợp lệ.
Hình 5. Mạch tạo tín hiệu báo valid cho ngõ ra data_out
1.2 RTL code và Testbench

Pass (nếu có): nguyenquanicd
Source File:
  • sfifo_v2.v - RTL code
  • sfifo_define.h - chứa các định nghĩa để cấu hình trước khi tổng hợp. Nếu bạn không muốn sử dụng phần tín hiệu hay mạch nào thì chỉ cần comment che định nghĩa đó đi. Thêm 2 định nghĩa mới:
    • `define WRITE_FULL_EN - Cho phép hỗ trợ ghi khi FULL
    • `define READ_EMPTY_EN - Cho phép hỗ trợ đọc khi EMPTY
  • sfifo_parameter.h - chứa thông số cấu hình dung lượng FIFO
  • tb_sfifo_v2.v - một basic testbench
Lưu ý:
  • Chỉ hỗ trợ ghi khi FULL và đọc khi EMPTY khi không định nghĩa TWO_CLOCK (nghĩa là chỉ sử dụng 1 xung clock)
1.3 Tổng hợp trên FPGA
Cấu hình trong file sfifo_define.h
`define EMPTY_SIGNAL
`define FULL_SIGNAL
`define SET_LOW_EN
`define SET_HIGH_EN
`define LOW_TH_SIGNAL
`define HIGH_TH_SIGNAL
`define OV_SIGNAL
`define UD_SIGNAL
//`define OUTPUT_REG
//`define TWO_CLOCK
//The new defines of the version 2
//Only use the following options when TWO_CLOCK is not defined
`define WRITE_FULL_EN

`define READ_EMPTY_EN



2. LIFO (STACK) đồng bộ
2.1 Phân tích
Đầu tiên các bạn hãy tham khảo bài LIFO đồng bộ phiên bản 1 trong wesite này.

Trong phiên bản này, gọi là phiên bản 1, LIFO không hỗ trợ việc đọc và ghi cùng một thời điểm, nghĩa là khi wr=rd=1 thì LIFO không hoạt động. Để hỗ trợ chức năng này, một số điều chỉnh thiết kế cần được thực hiện.
Hình 6. Chuyển tiếp dữ liệu khi wr=rd=1

Mạch tạo dữ liệu ngõ ra LIFO
thực hiện chuyển tiếp dữ liệu từ ngõ vào đến ngõ ra khi có sự kiện đọc/ghi cùng xuất hiện.
Hình 7. Mạch tạo ngõ ra LIFO phiên bản 2
Mạch tạo tín hiệu cho phép đọc/ghi LIFO chỉ tích cực tín hiệu lifo_we hoặc lifo_re khi không có sự kiện đọc/ghi đồng thời.
Hình 8. Mạch tạo tín hiệu cho phép đọc/ghi LIFO phiên bản 2
Mạch set/clear tín hiệu báo overflow và underflow cũng được điều chỉnh để:

  • Không báo overflow khi xuất hiện sự kiện ghi vào LIFO khi đang đầy nếu sự kiện đọc cũng xuất hiện, vì lúc này dữ liệu ghi vào LIFO được chuyển tiếp đến ngõ ra LIFO
  • Không báo underflow khi xuất hiện sự kiện đọc từ LIFO khi đang rỗng nếu sự kiện ghi cũng xuất hiện, vì lúc này dữ liệu ghi vào LIFO được chuyển tiếp đến ngõ ra LIFO

Hình 9. Mạch tạo tín hiệu set/clear tín hiệu báo trạng thái overflow và underflow
Mạch tạo tín hiệu báo dữ liệu ngõ ra LIFO hợp lệ được tích cực khi dữ liệu được đọc ở ngõ ra LIFO là hợp lệ.
Hình 10. Mạch tạo tín hiệu báo giá trị hợp lệ của ngõ ra LIFO
2.2 RTL code và testbench


Pass (nếu có): nguyenquanicd

Source File:
  • slifo_v2.v - RTL code
  • slifo_define.h - chứa các định nghĩa để cấu hình trước khi tổng hợp. Nếu bạn không muốn sử dụng phần tín hiệu hay mạch nào thì chỉ cần comment che định nghĩa đó đi. Thêm định nghĩa mới:
    • `define WR_SAME_TIME_EN - cho phép chức năng đọc/ghi đồng thời được tạo ra
  • slifo_parameter.h - chứa thông số cấu hình dung lượng LIFO
  • tb_slifo_v2.v - một basic testbench
2.3 Tổng hợp trên FPGA
Cấu hình trong file slifo_define.h
`define EMPTY_SIGNAL
`define FULL_SIGNAL
`define SET_LOW_EN
`define SET_HIGH_EN
`define LOW_TH_SIGNAL
`define HIGH_TH_SIGNAL
`define OV_SIGNAL
`define UD_SIGNAL
`define OUTPUT_REG
//The new defines of the version 2
`define WR_SAME_TIME_EN

*Nếu có bất cứ ý kiến cần trao đổi, các bạn có thể comment dưới bài viết 

Thứ Năm, 31 tháng 8, 2017

[IP core] LIFO (STACK) đồng bộ có thể cấu hình được

Bài này giới thiệu về thiết kế LIFO (Last-In Fist-Out) đồng bộ có thể cấu hình được độ rộng dữ liệu và dung lượng lưu trữ trước khi tổng hợp. LIFO đồng bộ thường được sử dụng như bộ đệm lưu trữ dữ liệu tạm thời ví dụ như chức năng của STACK trong CPU.

1. Giới thiệu
Khác với FIFO, LIFO hoạt động theo nguyên tắc dữ liệu được lưu vào bộ nhớ sau cùng sẽ được lấy ra trước tiên và ngược lại. LIFO cũng có hai thông số quan trọng là số lượng ô nhớ và độ rộng dữ liệu để xác định dung lượng LIFO. Hai thông số đặc trưng của LIFO là:
  • Số lượng ô nhớ hay còn gọi là độ sâu của LIFO
  • Độ rộng ô nhớ tương ứng với độ rộng dữ liệu được ghi vào và đọc ra.
Hai thông số trên sẽ cho biết dung lượng của LIFO. Ví dụ, LIFO có 4 ô nhớ, mỗi ô nhớ lưu 8 bit dữ liệu thì dung lượng LIFO là 4*8 = 32 bit hoặc 4 byte. Để định địa chỉ các ô nhớ của LIFO, một phương pháp thường dùng là sử dụng con trỏ ghi và con trỏ đọc. Con trỏ ở đây thực chất là bộ đếm tuần tự.

Hình 1. Hoạt động của LIFO
LIFO thường có các tín hiệu báo trạng thái hoạt động của chúng như:
  • Trạng thái rỗng: không chưa bất cứ dữ liệu nào
  • Trạng thái đầy: toàn bộ dung lượng đã được lấp đầy dữ liệu
  • Trạng thái overflow: Đang đầy những vẫn tiếp tục được ghi vào. Vào trường hợp này, dữ liệu trong LIFO vẫn giữ nguyên chứ không bị ghi đè bởi dữ liệu mới
  • Trạng thái underflow: Đang rỗng những vẫn tiếp tục được đọc. Vào trường hợp này, ngõ ra dữ liệu sẽ giữ giá trị dữ liệu cũ gần nhất.
  • Trạng thái dưới hoặc trên một mức giá trị ngưỡng. Ví dụ, số dữ liệu trong LIFO nhỏ hơn hoặc bằng 4 thì tích cực báo ngưỡng dưới. Giá trị 4 ở đây là ngưỡng dưới.
2. Sơ đồ tín hiệu giao tiếp
Hình 1. Sơ đồ tín hiệu giao tiếp của LIFO
  1. Ngõ vào (input)
    1. clk:    xung clock
    2. rst_n: tín hiệu reset tích cực mức thấp
    3. wr:     tín hiệu ghi dữ liệu vào LIFO 
    4. rd:      tín hiệu đọc dữ liệu từ LIFO 
    5. data_in[DATA_WIDTH-1:0]: bus dữ liệu ghi có số bit là DATA_WIDTH
    6. low_th[POINTER_WIDTH-1:0]: tín hiệu cấu hình mức ngưỡng dưới có số bit là POINTER_WIDTH.
    7. high_th[POINTER_WIDTH-1:0]: tín hiệu cấu hình mức ngưỡng trên có số bit là POINTER_WIDTH.
  2. Ngõ ra (output)
    1. lifo_empty:   tín hiệu báo LIFO rỗng
    2. lifo_full:       tín hiệu báo LIFO đầy
    3. lifo_low_th:  tín hiệu báo LIFO thấp hơn mức ngưỡng dưới 
    4. lifo_high_th:  tín hiệu báo LIFO cao hơn hoặc bằng mức ngưỡng trên
    5. lifo_ov: tín hiệu báo LIFO bị overflow
    6. lifo_ud: tín hiệu báo LIFO bị underflow
    7. lifo_data_out[DATA_WIDTH-1:0]: bus dữ liệu đọc có số bit là DATA_WIDTH
3. Các thông số cấu hình
LIFO sẽ có các thông số cấu hình như sau:
Thông số định nghĩa chức năng:
  1. EMPTY_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo trạng thái rỗng của LIFO , lifo_empty
  2. FULL_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo trạng thái đầy của LIFO, lifo_full
  3. SET_LOW_EN: Thông số cho phép tạo tín hiệu ngõ vào dùng để thiết lập giá trị ngưỡng dưới. Chú ý, thông số này chỉ được định nghĩa khi LOW_TH_SIGNAL được sử dụng.
  4. SET_HIGH_EN: Thông số cho phép tạo tín hiệu ngõ vào dùng để thiết lập giá trị ngưỡng trên. Chú ý, thông số này chỉ được định nghĩa khi HIGH_TH_SIGNAL được sử dụng.
  5. LOW_TH_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo dưới một mức ngưỡng được thiết lập sẵn, lifo_low_th
  6. HIGH_TH_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo dưới mức ngưỡng được thiết lập của LIFO, lifo_high_th
  7. OV_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo overflow, lifo_ov
  8. UD_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo underflow, lifo_ud
  9. OUTPUT_REG: Chèn thanh ghi tại ngõ ra của bus dữ liệu LIFO
Thông số cấu hình giá trị:
  1. DATA_WIDTH: Thiết lập số bit của 1 ô dữ liệu LIFO , tương ứng là độ rộng của bus dữ liệu ghi/đọc
  2. PTR_WIDTH: Thiết lập độ rộng của con trỏ ghi/đọc. Giá trị này sẽ quyết định số ô nhớ của LIFO theo công thức "Số ô nhớ của LIFO =2^POINTER_WIDTH. Ví dụ, cấu hình POINTER_WIDTH = 3 thì số ô nhớ của LIFO là 2^3 = 8 ô.
4. Phân tích cấu trúc LIFO
Lưu ý, các phần tô đỏ là các phần có thể được lựa chọn sẽ được tạo ra hoặc không tạo ra khi tổng hợp thông qua việc định nghĩa các thông số tương ứng. Ví dụ, nếu bạn muốn có tín hiệu slifo_ov thì hãy định nghĩa từ khóa OV_SIGNAL, ngược lại, nếu không định nghĩa OV_SIGNAL thì tín hiệu này sẽ không được tạo ra.
LIFO này không cho phép đọc và ghi đồng thời, nghĩa là một thời điểm chỉ đọc hoặc ghi.
4.1 Mạch nguyên lý tổng quan
Hình 2. Mạch nguyên lý của LIFO sử dụng 1 con trỏ quản lý đọc và ghi
4.2 Con trỏ đọc/ghi và cờ báo trạng thái

Con trỏ đọc và ghi đối với thiết kế LIFO này chỉ là 1 bộ đếm duy nhất hoạt động theo nguyên tắc:
  • Tăng 1 khi ghi
  • Giảm 1 khi đọc
Mạch con trỏ đọc ghi có thể thực hiện theo 2 cách:
  • Sử dụng 1 bộ cộng và 1 bộ trừ
  • Sử dụng 1 bộ cộng và 1 MUX. Cách này thực hiện dựa trên nguyên lý biến đổi bộ trừ thành mạch cộng bù 2. Sử dụng mạch này sẽ bớt được 1 bộ cộng và thay bằng bộ MUX và tận dụng cờ nhớ (carry flag) có sẵn của các bộ cộng thư viện tổng hợp ASIC nên tài nguyên sẽ giảm hơn.

Hình 3. Mạch con trỏ sử dụng 1 bộ cộng và 1 bộ trừ
Hình 4. Mạch nguyên lý sử dụng 1 bộ cộng và tạo các cờ báo trạng thái
4.3 Cờ báo trạng thái overflow và underflow
Hình 5. Cờ báo trạng thái overflow và underflow
5. RTL code và testbench


Pass (nếu có): nguyenquanicd

Source File:
  1. slifo.v - RTL code
  2. slifo_define.h - chứa các định nghĩa để cấu hình trước khi tổng hợp. Nếu bạn không muốn sử dụng phần tín hiệu hay mạch nào thì chỉ cần comment che định nghĩa đó đi.
  3. slifo_parameter.h - chứa thông số cấu hình dung lượng LIFO
  4. tb_slifo - một basic testbench
6. Kết quả
Một số hình ảnh tổng hợp trên Quartus II để các bạn có thể hình dung về cách cấu hình.
6.1 Cấu hình 1

Yêu cầu:
  1. Không sử dụng bất cứ tín hiệu báo trạng thái nào
  2. Ngõ ra không chốt thanh ghi
  3. Dung lượng 16 bit * 16 ô nhớ
slifo_define.h
//`define EMPTY_SIGNAL
//`define FULL_SIGNAL
//`define SET_LOW_EN
//`define SET_HIGH_EN
//`define LOW_TH_SIGNAL
//`define HIGH_TH_SIGNAL
//`define OV_SIGNAL
//`define UD_SIGNAL
//`define OUTPUT_REG

slifo_parameter.h
parameter DATA_WIDTH    = 16;
parameter POINTER_WIDTH = 4;

Kết quả

6.2 Cấu hình 2

Yêu cầu:
  1. Sử dụng tín hiệu báo LIFO đầy và rỗng
  2. Sử dụng tín hiệu báo ngưỡng trên, ngưỡng dưới và có tín hiệu đầu vào để cấu hình giá trị ngưỡng
  3. Ngõ ra chốt thanh ghi
  4. Dung lượng 4 bit * 16 ô nhớ
slifo_define.h
`define EMPTY_SIGNAL
`define FULL_SIGNAL
`define SET_LOW_EN
`define SET_HIGH_EN
`define LOW_TH_SIGNAL
`define HIGH_TH_SIGNAL
//`define OV_SIGNAL
//`define UD_SIGNAL
`define OUTPUT_REG

slifo_parameter.h
parameter DATA_WIDTH    = 4;
parameter POINTER_WIDTH = 4;

Kết quả

Mọi góp ý trao đổi, bạn đọc có thể comment dưới bài viết.

Thứ Tư, 30 tháng 8, 2017

[IP core] FIFO đồng bộ có thể cấu hình được

Bài này giới thiệu về thiết kế FIFO đồng bộ có thể cấu hình được độ rộng dữ liệu và dung lượng lưu trữ trước khi tổng hợp. FIFO đồng bộ thường được sử dụng như bộ đệm lưu trữ dữ liệu tạm thời giữa hai miền có tốc độ xử lý dữ liệu khác nhau.

1. Giới thiệu

Lưu ý, tốc độ xử lý dữ liệu khác nhau không có ý nghĩa là xung clock khác nhau hay bất đồng bộ với nhau. Trong trường hợp này, cả hai miền đều sử dụng các xung clock đồng bộ với nhau nhưng vì một miền xử lý nhanh, một miền xử lý chậm nên dữ liệu không thể truyền liên tục giữa hai miền. Giải pháp cho vấn đề này là sử dụng một bộ nhớ lưu dữ liệu tạm thời giữa hai miền để lưu những dữ liệu chưa được xử lý kịp.

FIFO hoạt động theo nguyên tắc dữ liệu được lưu vào trước sẽ là dữ liệu được lấy ra trước. Nghĩa là, thứ tự dữ liệu được đọc ra giống như thứ tự dữ liệu được ghi vào. Hai thông số đặc trưng của FIFO là:
  • Số lượng ô nhớ hay còn gọi là độ sâu của FIFO
  • Độ rộng ô nhớ tương ứng với độ rộng dữ liệu được ghi vào và đọc ra.
Hai thông số trên sẽ cho biết dung lượng của FIFO. Ví dụ, FIFO có 4 ô nhớ, mỗi ô nhớ lưu 8 bit dữ liệu thì dung lượng FIFO là 4*8 = 32 bit hoặc 4 byte. Để định địa chỉ các ô nhớ của FIFO, một phương pháp thường dùng là sử dụng con trỏ ghi và con trỏ đọc. Con trỏ ở đây thực chất là bộ đếm tuần tự.
Hình 1. Minh họa hoạt động của FIFO
2. Sơ đồ tín hiệu giao tiếp FIFO
FIFO thiết kế sẽ có các tín hiệu giao tiếp như sau:
Hình 2. Sơ đồ tín hiệu giao tiếp của FIFO đồng bộ tên SFIFO
  1. Ngõ vào (input)
    1. clk_rd:    xung clock đọc dữ liệu
    2. clk_wr: xung clock ghi dữ liệu, đồng bộ với xung clock đọc clk_rd
    3. rst_n: tín hiệu reset tích cực mức thấp
    4. wr:     tín hiệu ghi dữ liệu vào FIFO
    5. rd:      tín hiệu đọc dữ liệu từ FIFO
    6. data_in[DATA_WIDTH-1:0]: bus dữ liệu ghi có số bit là DATA_WIDTH
    7. low_th[TH_WIDTH-1:0]: tín hiệu cấu hình mức ngưỡng dưới có số bit là TH_WIDTH.
    8. high_th[TH_WIDTH-1:0]: tín hiệu cấu hình mức ngưỡng trên có số bit là TH_WIDTH.
  2. Ngõ ra (output)
    1. sfifo_empty:   tín hiệu báo FIFO rỗng
    2. sfifo_full:       tín hiệu báo FIFO đầy
    3. sfifo_low_th:  tín hiệu báo FIFO thấp hơn mức ngưỡng dưới 
    4. sfifo_high_th:  tín hiệu báo FIFO cao hơn hoặc bằng mức ngưỡng trên
    5. sfifo_ov: tín hiệu báo FIFO bị overflow
    6. sfifo_ud: tín hiệu báo FIFO bị underflow
    7. sfifo_data_out[DATA_WIDTH-1:0]: bus dữ liệu đọc có số bit là DATA_WIDTH
3. Các thông số cấu hình
FIFO sẽ có các thông số cấu hình như sau:
Thông số định nghĩa chức năng:
  1. EMPTY_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo trạng thái rỗng của FIFO, sfifo_empty
  2. FULL_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo trạng thái đầy của FIFO, sfifo_full
  3. SET_LOW_EN: Thông số cho phép tạo tín hiệu ngõ vào dùng để thiết lập giá trị ngưỡng dưới. Chú ý, thông số này chỉ được định nghĩa khi LOW_TH_SIGNAL được sử dụng.
  4. SET_HIGH_EN: Thông số cho phép tạo tín hiệu ngõ vào dùng để thiết lập giá trị ngưỡng trên. Chú ý, thông số này chỉ được định nghĩa khi HIGH_TH_SIGNAL được sử dụng.
  5. LOW_TH_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo dưới một mức ngưỡng được thiết lập sẵn, sfifo_low_th
  6. HIGH_TH_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo dưới mức ngưỡng được thiết lập của FIFO, sfifo_high_th
  7. OV_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo overflow, sfifo_ov
  8. UD_SIGNAL: Thông số cho phép tạo tín hiệu ngõ ra báo underflow, sfifo_ud
  9. OUTPUT_REG: Chèn thanh ghi tại ngõ ra của bus dữ liệu FIFO
  10. TWO_CLOCK: Tạo tín hiệu xung clock đọc và xung clock ghi khác nhau. Lưu ý, cấu hình này sẽ tạo ra hai tín hiệu xung clock ngõ vào sử dụng cho quá trình đọc và quá trình ghi nhưng hai xung clock cấp cho FIFO vẫn phải là 2 xung clock đồng bộ.
Thông số cấu hình giá trị:
  1. DATA_WIDTH: Thiết lập số bit của 1 ô dữ liệu FIFO, tương ứng là độ rộng của bus dữ liệu ghi/đọc
  2. POINTER_WIDTH: Thiết lập độ rộng của con trỏ ghi/đọc. Giá trị này sẽ quyết định số ô nhớ của FIFO theo công thức "Số ô nhớ của FIFO=2^POINTER_WIDTH. Ví dụ, cấu hình POINTER_WIDTH= 3 thì số ô nhớ của FIFO là 2^3 = 8 ô.
  3. TH_WIDTH = POINTER_WIDTH
4. Phân tích cấu trúc FIFO
Lưu ý, các phần tô đỏ là các phần có thể được lựa chọn sẽ được tạo ra hoặc không tạo ra khi tổng hợp thông qua việc định nghĩa các thông số tương ứng. Ví dụ, nếu bạn muốn có tín hiệu sfifo_ov thì hãy định nghĩa từ khóa OV_SIGNAL, ngược lại, nếu không định nghĩa OV_SIGNAL thì tín hiệu này sẽ không được tạo ra.
4.1 Mạch nguyên lý tổng quan

4.2 Con trỏ ghi và đọc

4.3 Mạch báo trạng thái full/empty/overflow/underflow

4.4 Mạch báo trạng thái ngưỡng trên và ngưỡng dưới

5. RTL code và testbench

Link: dowload RTL code của SFIFO

Pass (nếu có): nguyenquanicd

Source File:
  1.  sfifo.v  - RTL code
  2.  sfifo_define.h - chứa các định nghĩa để cấu hình trước khi tổng hợp. Nếu bạn không muốn sử dụng phần tín hiệu hay mạch nào thì chỉ cần comment che định nghĩa đó đi.
  3.  sfifo_parameter.h - chứa thông số cấu hình dung lượng FIFO
  4.  tb_sfifo - một basic testbench

6. Kết quả
Một số hình ảnh tổng hợp trên Quartus II để các bạn có thể hình dung về cách cấu hình.

6.1 Cấu hình 1

Yêu cầu:
  1. Không sử dụng bất cứ tín hiệu báo trạng thái nào
  2. Ngõ ra không chốt thanh ghi
  3. Sử dụng 1 nguồn xung clock
  4. Dung lượng 8 bit * 8 ô nhớ
sfifo_define.h
//`define EMPTY_SIGNAL
//`define FULL_SIGNAL
//`define SET_LOW_EN
//`define SET_HIGH_EN
//`define LOW_TH_SIGNAL
//`define HIGH_TH_SIGNAL
//`define OV_SIGNAL
//`define UD_SIGNAL
//`define OUTPUT_REG
//`define TWO_CLOCK

sfifo_parameter.h
parameter DATA_WIDTH    = 8;
parameter POINTER_WIDTH = 3;

Kết quả

6.2 Cấu hình 2

Yêu cầu:
  1. Sử dụng tín hiệu báo FIFO đầy và rỗng
  2. Ngõ ra chốt thanh ghi
  3. Sử dụng 2 nguồn xung clock đồng bộ
  4. Dung lượng 8 bit * 16 ô nhớ
sfifo_define.h
`define EMPTY_SIGNAL
`define FULL_SIGNAL
//`define SET_LOW_EN
//`define SET_HIGH_EN
//`define LOW_TH_SIGNAL
//`define HIGH_TH_SIGNAL
//`define OV_SIGNAL
//`define UD_SIGNAL
`define OUTPUT_REG
`define TWO_CLOCK

sfifo_parameter.h
parameter DATA_WIDTH    = 8;
parameter POINTER_WIDTH = 4;

Kết quả

6.3 Cấu hình 3

Yêu cầu: Thiết lập đầy đủ chức năng
  1. Sử dụng tất cả tín hiệu báo trạng thái trong đó hai trạng thái báo ngưỡng trên và dưới có thể lập trình được (có SET_LOW_EN và SET_HIGH_EN)
  2. Ngõ ra chốt thanh ghi
  3. Sử dụng 2 nguồn xung clock đồng bộ
  4. Dung lượng 32 bit * 16 ô nhớ
sfifo_define.h
`define EMPTY_SIGNAL
`define FULL_SIGNAL
`define SET_LOW_EN
`define SET_HIGH_EN
`define LOW_TH_SIGNAL
`define HIGH_TH_SIGNAL
`define OV_SIGNAL
`define UD_SIGNAL
`define OUTPUT_REG
`define TWO_CLOCK

sfifo_parameter.h
parameter DATA_WIDTH    = 8;
parameter POINTER_WIDTH = 4;

Kết quả


Note: Hãy trao đổi dưới bài viết nếu bạn gặp bất cứ vấn đề gì khi sử dụng.

Chủ Nhật, 27 tháng 8, 2017

[IP core] Lõi IP mã hóa giải mã DES

Bài viết phân tích và mô tả một thiết kế lõi IP thực hiện mã hóa và giải mã DES. Đầu tiên, chi tiết về bộ mã hóa sẽ được phân tích. Sau đó, những điểm khác biệt của bộ giải mã sẽ được phân tích để tích hợp bộ mã hóa để tạo ra một thiết kế đầy đủ chức năng mã hóa và giải mã DES. Thiết kế có thể được hiện thực trên FPGA hoặc ASIC.


Để hiểu rõ, các bạn hãy đọc từng bước được mô tả trong bài trên và so sánh với từng bước được phân tích trong bài này.

1. Lõi mã hóa DES
2.1 Sơ đồ tín hiệu
Hình 1. Tín hiệu giao tiếp của bộ mã hóa DES
Hình 2. Mô tả các tín hiệu giao tiếp của lõi mã hóa DES
Chú ý: Trong chuẩn DES, việc đánh số thứ tự của chuỗi bit được thực hiện từ trái qua phải. Một tín hiệu được khai báo theo thứ tự từ MSB đến LSB nên trong phần thiết kế, giá trị các bảng hoán vị được thay đổi lại theo thứ tự bit này.Ví dụ: một tín hiệu được khai báo [63:0] thì bit 1 trong chuẩn DES ứng với bit 63, bit 2 ứng trong chuẩn mã hóa ứng với bit 62, ... và bit 64 trong chuẩn DES ứng với bit 0.
Các bạn cần lưu ý kỹ điều này trước khi đọc tiếp những phần mô tả dưới đây.

2.2 Mạch tính toán khóa vòng - KS
Mạch sử dụng 2 thanh ghi để lưu lại giá trị Cn và Dn sau mỗi lần tính toán.

2.2.1 Tính hoán vị PC-1
Hoán vị thực chất là thay đổi trật tự sắp xếp các bit theo quy định của chuẩn. Như đã lưu ý, trong hoán vị PC-1 sau đây, tại bảng tạo ra c0, vị trí ô MSB là bit des_key_in[7] ứng với bit thứ 57 theo cách đánh số từ trái sang phải của chuẩn. Tương tự, ở bảng tạo ra d0, vị trí MSB là des_key_in[1] ứng với bit thứ 63 theo cách đánh số từ trái sang phải của chuẩn.
Hình 3. Tính hoán vị PC-1 - chỉ số trong từng ô vuông là vị trí bit của tín hiệu des_key_in[63:0]
Ví dụ về đoạn code thực hiện hoán vị tạo ra c0:
assign c0 = {des_key_in[7], des_key_in[15], des_key_in[23], des_key_in[31], des_key_in[39], des_key_in[47], des_key_in[55],
des_key_in[63], des_key_in[6], des_key_in[14], des_key_in[22], des_key_in[30], des_key_in[38], des_key_in[46],
des_key_in[54], des_key_in[62], des_key_in[5], des_key_in[13], des_key_in[21], des_key_in[29], des_key_in[37],
des_key_in[45], des_key_in[53], des_key_in[61], des_key_in[4], des_key_in[12], des_key_in[20], des_key_in[28]};
Đoạn code trên rất đơn giản là thực hiện sắp xếp lại các bit.

2.2.2 Phép dịch trái
Mạch này sử dụng 2 thanh ghi để lưu giá trị thứ n của C và D. Mạch này lấy đầu vào là:
  • Giá trị C0 và D0 từ phép tính hoán vị PC-1
  • Giá trị Cn-1 và Dn-1 từ 2 thanh ghi
Mạch sử dụng các tín hiệu điều khiển sau:
  • rkey_sel để lựa chọn khi nào sử dụng giá trị C0/D0 và khi nào sử dụng giá trị từ thanh ghi
  • shift_1 để xác định thời điểm dịch 1 bit và thời điểm dịch 2 bit
  • key_process để xác định thời điểm cần tính khóa vòng mới
Các tín hiệu trên sẽ được tạo ra từ các mạch khác và được mô tả trong các phần tiếp theo của bài này.
Hình 4. Mạch dịch trái 
2.2.3 Tính hoán vị PC-2
Ngõ ra mạch dịch trái trên đây được hoán vị theo bảng PC_2 như sau để tạo ra khóa vòng.
Hình 5. Mạch hoán vị PC-2
2.3 Mạch tính hàm mã hóa f(R,K)
2.3.1 Tính hoán vị E
Hoán vị E sẽ tạo ra giá trị đầu vào cho các bảng S.
Hình 6. Hoán vị E và XOR với khóa vòng, kết quả được chia thành 8 nhóm 6 bit để dùng cho 8 bảng S
2.3.2 Thực thi tra bảng S
Tra bảng S thực chất là thay thế giá trị đầu vào bằng một giá trị khác được quy định trong bảng.
Hình 6. Thực thi tra bảng S
Một đoạn code RTL thực thi tra bảng S1 sẽ được thể hiện dưới đây. Trong đó, mỗi phát biểu case(s1_in[4:1]) tương ứng với việc tra 1 hàng của bảng S1:
always @ (*) begin
case ({s1_in[5], s1_in[0]})
2'b00: begin
case (s1_in[4:1])
4'd0: s1_out = 4'd14;
4'd1: s1_out = 4'd4;
4'd2: s1_out = 4'd13;
4'd3: s1_out = 4'd1;
4'd4: s1_out = 4'd2;
4'd5: s1_out = 4'd15;
4'd6: s1_out = 4'd11;
4'd7: s1_out = 4'd8;
4'd8: s1_out = 4'd3;
4'd9: s1_out = 4'd10;
4'd10: s1_out = 4'd6;
4'd11: s1_out = 4'd12;
4'd12: s1_out = 4'd5;
4'd13: s1_out = 4'd9;
4'd14: s1_out = 4'd0;
default: s1_out = 4'd7;
endcase
end
2'b01: begin
case (s1_in[4:1])
4'd0: s1_out = 4'd0;
4'd1: s1_out = 4'd15;
4'd2: s1_out = 4'd7;
4'd3: s1_out = 4'd4;
4'd4: s1_out = 4'd14;
4'd5: s1_out = 4'd2;
4'd6: s1_out = 4'd13;
4'd7: s1_out = 4'd1;
4'd8: s1_out = 4'd10;
4'd9: s1_out = 4'd6;
4'd10: s1_out = 4'd12;
4'd11: s1_out = 4'd11;
4'd12: s1_out = 4'd9;
4'd13: s1_out = 4'd5;
4'd14: s1_out = 4'd3;
default: s1_out = 4'd8;
endcase
end
2'b10: begin
case (s1_in[4:1])
4'd0: s1_out = 4'd4;
4'd1: s1_out = 4'd1;
4'd2: s1_out = 4'd14;
4'd3: s1_out = 4'd8;
4'd4: s1_out = 4'd13;
4'd5: s1_out = 4'd6;
4'd6: s1_out = 4'd2;
4'd7: s1_out = 4'd11;
4'd8: s1_out = 4'd15;
4'd9: s1_out = 4'd12;
4'd10: s1_out = 4'd9;
4'd11: s1_out = 4'd7;
4'd12: s1_out = 4'd3;
4'd13: s1_out = 4'd10;
4'd14: s1_out = 4'd5;
default: s1_out = 4'd0;
endcase
end
default: begin
case (s1_in[4:1])
4'd0: s1_out = 4'd15;
4'd1: s1_out = 4'd12;
4'd2: s1_out = 4'd8;
4'd3: s1_out = 4'd2;
4'd4: s1_out = 4'd4;
4'd5: s1_out = 4'd9;
4'd6: s1_out = 4'd1;
4'd7: s1_out = 4'd7;
4'd8: s1_out = 4'd5;
4'd9: s1_out = 4'd11;
4'd10: s1_out = 4'd3;
4'd11: s1_out = 4'd14;
4'd12: s1_out = 4'd10;
4'd13: s1_out = 4'd0;
4'd14: s1_out = 4'd6;
default: s1_out = 4'd13;
endcase
end
endcase
end
2.3.3 Hoán vị P

Sau khi tra các bảng S, giá trị sẽ được hoán vị theo bảng P để tạo ra giá trị hàm f(R,K).
Hình 8. Hoán vị P để tạo giá trị hàm f(R,K)

Đoạn RTL code thực hiện hoán vị P:
assign p_in = {s1_out, s2_out, s3_out, s4_out, s5_out, s6_out, s7_out, s8_out};
assign f_value = {p_in[16], p_in[25], p_in[12], p_in[11],
p_in[3], p_in[20], p_in[4], p_in[15],
p_in[31], p_in[17], p_in[9], p_in[6],
p_in[27], p_in[14], p_in[1], p_in[22],
p_in[30], p_in[24], p_in[8], p_in[18],
p_in[0], p_in[5], p_in[29], p_in[23],
p_in[13], p_in[19], p_in[2], p_in[26],
p_in[10], p_in[21], p_in[28], p_in[7]};

2.4 Mạch thực hiện thuật toán mã hóa DES
2.4.1 Hoán vị đầu vào - IP và lựa chọn giá trị cho mỗi vòng lặp mã hóa
Hình 9. Thực hiện hoán vị đầu vào và lựa chọn giá trị
Hoán vị đầu vào tạo ra giá trị ngõ vào đầu tiên l0 và r0. Mạch sử dụng một tín hiệu điều khiển cipher_sel để chọn giá trị tính toán là l0-r0 hoặc giá trị ln-rn (đây là gí trị lưu trong thanh ghi).
Đoạn RTL code thực hiện hoán vị đầu vào và lựa chọn giá trị cho mỗi vòng lặp mã hóa như sau:
assign ip_input = {des_data[6], des_data[14], des_data[22], des_data[30], des_data[38], des_data[46], des_data[54], des_data[62],
des_data[4], des_data[12], des_data[20], des_data[28], des_data[36], des_data[44], des_data[52], des_data[60],
des_data[2], des_data[10], des_data[18], des_data[26], des_data[34], des_data[42], des_data[50], des_data[58],
des_data[0], des_data[8], des_data[16], des_data[24], des_data[32], des_data[40], des_data[48], des_data[56],
des_data[7], des_data[15], des_data[23], des_data[31], des_data[39], des_data[47], des_data[55], des_data[63],
des_data[5], des_data[13], des_data[21], des_data[29], des_data[37], des_data[45], des_data[53], des_data[61],
des_data[3], des_data[11], des_data[19], des_data[27], des_data[35], des_data[43], des_data[51], des_data[59],
des_data[1], des_data[9], des_data[17], des_data[25], des_data[33], des_data[41], des_data[49], des_data[57]};
assign l0 = ip_input[63:32];
assign r0 = ip_input[31:0];
assign l_input = (cipher_sel)? l0: ln;
assign r_input = (cipher_sel)? r0: rn;
2.4.2 Lưu giá trị Ln và Rn, thực hiện hoán vị đảo IP-1
Hình 10. Tính, lưu giá trị ln-rn và thực hiện hoán vị đảo IP-1
Ngõ ra l_input[31:0] từ mạch hoán vị và lựa chọn mục 2.4.1 được XOR với giá trị hàm f(R,K) trước khi lưu vào thanh ghi ln[31:0]. Giá trị 2 thanh ghi được ghép lại thành giá trị ngõ vào của hoán vị IP-1. Sau hoán vị IP-1, desc_ciphertext[63:0] là giá trị của mỗi vòng lặp mã hóa và là giá trị mã hóa nếu bộ mã hóa đang ở vòng lặp cuối cùng.
RTL code của thanh ghi ln và rn như sau:
always @ (posedge clk) begin
if (cipher_process) begin
ln <= `DELAY r_input;
rn <= `DELAY l_input ^ f_value;
end
end
RTL code của hoán vị đảo IP-1:
assign inv_p_input = {rn, ln};
assign desc_ciphertext = {inv_p_input[24], inv_p_input[56], inv_p_input[16], inv_p_input[48], inv_p_input[8], inv_p_input[40], inv_p_input[0], inv_p_input[32],
inv_p_input[25], inv_p_input[57], inv_p_input[17], inv_p_input[49], inv_p_input[9], inv_p_input[41], inv_p_input[1], inv_p_input[33],
inv_p_input[26], inv_p_input[58], inv_p_input[18], inv_p_input[50], inv_p_input[10], inv_p_input[42], inv_p_input[2], inv_p_input[34],
inv_p_input[27], inv_p_input[59], inv_p_input[19], inv_p_input[51], inv_p_input[11], inv_p_input[43], inv_p_input[3], inv_p_input[35],
inv_p_input[28], inv_p_input[60], inv_p_input[20], inv_p_input[52], inv_p_input[12], inv_p_input[44], inv_p_input[4], inv_p_input[36],
inv_p_input[29], inv_p_input[61], inv_p_input[21], inv_p_input[53], inv_p_input[13], inv_p_input[45], inv_p_input[5], inv_p_input[37],
inv_p_input[30], inv_p_input[62], inv_p_input[22], inv_p_input[54], inv_p_input[14], inv_p_input[46], inv_p_input[6], inv_p_input[38],
inv_p_input[31], inv_p_input[63], inv_p_input[23], inv_p_input[55], inv_p_input[15], inv_p_input[47], inv_p_input[7], inv_p_input[39]};
2.5 Mạch giám sát quá trình mã hóa

Phần mạch này tạo ra các tín hiệu điều khiển cho các mạch đã trình bày ở trên. Để giám sát quá trình mã hóa một bộ đếm 4 bit được sử dụng để xác định số lần lặp mã hóa. Đồng thời giá trị bộ đếm này được sử dụng để tạo thêm tín hiệu điều khiển khác. Ví dụ, tín hiệu shift_1 tích cực tại 4 vị trí ứng với lần lặp thứ 1 (bộ đếm bằng 0), thứ 2 (Bộ đếm bằng 1), thứ 9 (bộ đếm bằng 8) và thứ 16 (Bộ đếm bằng 15).
Hình 11. Bộ đếm giám sát quá trình mã hóa và tín hiệu xác định quá trình tính khóa vòng key_process
3. Bộ giải mã DES
Bộ giải mã được thực hiện dựa trên cấu trúc bộ mã hóa. Phần này chỉ nhấn mạnh các điểm được điều chỉnh để tích hợp phần giải mã vào mạch mã hóa để tạo ra một thiết kế có chức năng mã háo và giải mã hoàn chỉnh.
3.1 Tín hiệu giao tiếp
Tín hiệu giao tiếp được điều chỉnh như sau:
  • Tách tín hiệu des_cipher_en thành 2 tín hiệu:
    • des_encipher_en: Khởi động quá trình mã hóa
    • des_decipher_en: Khởi động quá trình giải mã
  • Tín hiệu des_ciphertext[63:0] được chuyển thành des_result[63:0] và tín hiệu này sẽ là giá trị bản mã của quá trình mã hóa hoặc bản rõ của quá trình giải mã
Hình 12. Sơ đồ tín hiệu giao tiếp của lõi tích hợp chức năng giải mã
3.2 Mạch tính khóa vòng
Mạch này điều chỉnh:
  • Tín hiệu rkey_sel ở mạch tạo cin và din
  • Điều chỉnh mạch của thanh ghi cn và dn
Hình 13. Mạch tính khóa vòng

3.3 Mạch tính hàm mã hóa f(R,K)

Không có sự thay đổi

3.4 Mạch thực hiện thuật toán giải mã

Điều chỉnh mạch tạo l_input[31:0] và r_input[31:0].
Hình 14. Mạch tạo ngõ vào cho bảng hoán vị IP-1
3.5 Mạch tạo giá trị ngõ ra bộ mã hóa và giải mã DES
Điều chỉnh thêm cổng OR ở mạch MUX tạo ngõ vào bảng hoán vị IP-1.
Hình 15. Mạch tạo ngõ ra bộ mã hóa và giải mã DES
3.6 Mạch giám sát quá trình giải mã và mã hóa
Mạch này có nhiều thay đổi để tách quá trình mã hóa và giải mã riêng. bạn đọc có thể so sánh và tự tìm hiểu.
Hình 16. Mạch giám sát quá trình mã hóa và giải mã
Một điểm khác biệt là, khi quá trình giải mã được khởi động, quá trình tính khóa vòng sẽ chạy để tính ra khóa vòng K16. Sau đó, quá trình giải mã mới thực sự được khởi động.

4. RTL code và testbench


pass (nếu có): nguyenquanicd

Mọi ý kiến trao đổi cá bạn có thể post dưới bài viết.