Thứ Sáu, 15 tháng 11, 2019

[JTAG] Bài 5 - Chế độ lập trình bộ nhớ programming

Bài viết này giải thích việc làm thế nào chế độ lập trình (programming) bộ nhớ trên chip (on-chip memory) có thể tích hợp với giao thức JTAG. Việc giải thích sẽ được cụ thể hóa bằng cấu trúc phần cứng và RTL code.

Danh sách bài viết trước đó:


1) Tổng quan
Như đã trình bày trong các bài viết trước, JTAG là một chuẩn mở, cho phép mở rộng mã lệnh để và tích hợp thêm các chức năng khác ngoài chuẩn IEEE 1149.1. Lập trình bộ nhớ trên chip (programming) là một trong các chứng năng thường thấy trên các vi điều khiển có bộ nhớ chương trình bên trong chip.
Programming là quá trình sử dụng giao tiếp JTAG để đọc, ghi và xóa bộ nhớ chương trình và bộ nhớ dữ liệu bên trong chip vi điều khiển (MCU). Đây là loại bộ nhớ không bay hơi (non-volatile memory) như ROM, Flash, FRAM … dùng để lưu chương trình và dữ liệu mà MCU sẽ thực thi khi được cấp nguồn.
Giao thức JTAG chỉ quy định bộ tín hiệu giao tiếp và cách thức hoạt động của bộ điều khiển TAP thông qua bộ tín hiệu này. JTAG không quy định hay giới hạn hoạt động của các chức năng được liên kết với nó. Trong trường hợp này, chức năng lập trình bộ nhớ trên chip được liên kết đến bộ điều khiển giao thức JTAG. Như vậy, một phần logic chuyển đổi, tạm gọi là converter, giữa giao tiếp JTAG và giao tiếp bộ nhớ phải tồn tại giữa bộ điều khiển JTAG và bộ nhớ.
Hình 1: Kết nối giữa JTAG và bộ nhớ trên chip
Tùy vào từng chip, giao tiếp giữa converter và bộ điều khiển JTAG sẽ khác nhau. Đường đi từ JTAG đến bộ nhớ cũng sẽ khác nhau nên converter có thể là một hoặc một nhóm các thành phần chuyển đổi giao thức từ JTAG đến bộ nhớ.
Hình 2: Một cấu trúc converter gồm AXI bus, APB bus và memory controller
Trong hình minh họa trên đây, bộ điều khiển JTAG là một master của bus AXI trong khi bộ nhớ là một slave của bus APB. Converter bao gồm bus AXI, bus APB và bộ điều khiển bộ nhớ (memory controller). Converter thực hiện chuyển đổi giao thức từ AXI->APB->bộ nhớ.
Hình 3: Một cấu trúc converter gồm APB bus và memory controller
Hình minh họa trên đây là một ví dụ khác, bộ điều khiển JTAG là một master của bus APB. Bộ nhớ là một slave của bus APB.
Lưu ý, bus AXI và bus APB trên đây là được hiểu là một bus kết nối đến nhiều master và nhiều slave nhưng trong hình minh họa chỉ vẽ đường từ JTAG đến bộ nhớ.
Hình 4: Một cấu trúc converter chỉ gồm memory controller
Trong hình minh họa trên đây, bộ điều khiển JTAG nối trực tiếp đến bộ điều khiển bộ nhớ thông qua một giao thức tùy chọn bất kỳ.
Phần tiếp theo sẽ trình bày một thiết kế cụ thể để giải thích làm thế nào chức năng programming có thể được sử dụng thông qua giao thức JTAG.
2) Bộ nhớ trên chip (on-chip memory)
Giả sử chúng ta cần thiết kế đọc/ghi một bộ nhớ trên chip có dung lượng 256x16 bit. Các tín hiệu giao tiếp của bộ nhớ như sau:
Hình 5: Sơ đồ tín hiệu của bộ nhớ trên chip
Quá trình ghi/đọc như sau:
  • Ghi dữ liệu: Các tín hiệu điều khiển mem_sel=1, mem_we=1, mem_addr[7:0] mem_wdata[15:0] phải giữ ổn định trong một khoảng thời gian tối thiểu là Twrite.
Hình 6: Ghi dữ liệu vào bộ nhớ
  • Đọc dữ liệu: Các tín hiệu điều khiển mem_sel=1, mem_we=0 mem_addr[7:0] phải giữ ổn định trong một khoảng thời gian lớn hơn Ttrans thì dữ liệu mới xuất hiện tại ngõ ra mem_rdata[15:0].
Hình 7: Đọc dữ liệu từ bộ nhớ
3) Phân tích sơ đồ khối thiết kế
Vì mục đích chính là hiểu làm thế nào chức năng programming có thể tích hợp với giao thức JTAG nên mô hình converter được chọn chỉ có memory controller. Cấu trúc hệ thống gồm các thành phần sau:
  • JTAG controller, tên module là jtag_ctrl, sẽ nhận các tín hiệu điều khiển từ giao tiếp JTAG và tạo ra các tín hiệu yêu cầu memory controller truy xuất bộ nhớ.
  • Memory controller, tên module là mem_ctrl, sẽ nhận các yêu cầu đọc/ghi từ JTAG controller và điều khiển việc đọc/ghi bộ nhớ theo đúng quy định về định thời.
  • On-chip memory, tên module là mem_model, là mô hình bộ nhớ trên chip đáp ứng yêu cầu đọc/ghi từ memory controller.
Trong các thành phần trên, jtag_ctrl mem_ctrl là hai thành phần code có thể tổng hợp được còn mem_model chỉ là mô hình bộ nhớ dùng để mô phỏng và không thể tổng hợp được.
Hình 8: Các miền clock trong thiết kế
Hệ thống dùng hai clock bất đồng bộ là clock JTAG (tck) được cập từ một bus master bên ngoài chip và clock hệ thống (clk) được cấp từ bộ tạo clock bên trong chip. Xung clock tck cấp cho các thành phần logic của JTAG controller còn clk cấp cho các thành phần của memory controller.
Hình 9: Sơ đồ tín hiệu giao tiếp giữa các khối chức năng
Bảng 1: Các tín hiệu giao tiếp giữa các khối
TT
Tên
Mô tả
1
tck
Clock đồng bộ của giao thức JTAG
2
tms
Điều khiển hoạt động của bộ điều khiển TAP, cụ thể là FSM của bộ điều khiển TAP.
Đồng bộ theo tck.
3
tdi
Dữ liệu vào nối tiếp.
Đồng bộ theo tck.
4
tdo
Dữ liệu ra nối tiếp.
Đồng bộ theo tck.
5
jtag_rst_n
Reset bộ điều khiển JTAG.
Tích cực mức thấp.
6
sys_rst_n
Reset của hệ thống dùng để reset bộ điều khiển bộ nhớ.
Tích cực mức thấp.
7
clk
Clock đồng bộ của hệ thống.
8
sel
Yêu cầu memory controller truy xuất bộ nhớ.
Đồng bộ theo clock tck.
9
we
Yêu cầu ghi bộ nhớ. Tín hiệu này chỉ hợp lệ khi sel=1.
Đồng bộ theo clock tck.
10
addr[7:0]
Địa chi truy xuất bộ nhớ. Tín hiệu này chỉ hợp lệ khi sel=1.
Đồng bộ theo clock tck.
11
wdata[15:0]
Dữ liệu ghi đến bộ nhớ. Tín hiệu này chỉ hợp lệ khi sel=1.
Đồng bộ theo clock tck.
12
rdata[15:0]
Dữ liệu đọc từ bộ nhớ. Tín hiệu này chỉ hợp lệ khi sel=1.
Đồng bộ theo clock clk.
13
ready
Tín hiệu báo memory controller đang sẵn sàng nhận yêu cầu truy xuất mới. Tín hiệu này sẽ không tích cực trong suốt quá trình memory controller đang đọc hoặc ghi bộ nhớ.
Đồng bộ theo clock clk.
14
mem_sel
Tín hiệu chọn bộ nhớ.
Đồng bộ theo clock clk.
15
mem_we
Tín hiệu cho phép ghi.
Đồng bộ theo clock clk.
16
mem_addr[7:0]
Địa chỉ truy xuất.
Đồng bộ theo clock clk.
17
mem_wdata[15:0]
Dữ liệu ghi.
Đồng bộ theo clock clk.
18
mem_rdata[15:0]
Dữ liệu đọc.
Được trả từ bộ nhớ và bất đồng bộ với clk.

Đối với yêu cầu thiết kế đặt ra, những vấn đề quan trọng người thiết kế cần phải làm rõ là:
  • Cơ chế điều khiển memory controller của JTAG
  • Cơ chế điều khiển bộ nhớ của memory controller dựa trên các sơ đồ định thời của bộ nhớ.
Thực tế, hai điều này phải được phân tích trước khi có sơ đồ các tín hiệu chi tiết như đã trình bày ở mục trên.

4) Cơ chế bắt tay giữa JTAG controller và memory controller

Chúng ta cần trả lời câu hỏi “memory controller cần thông gì để có thể truy xuất bộ nhớ?”. Điều này có thể suy ra từ các tín hiệu giao tiếp bộ nhớ. Sau bước này, các tín hiệu sau đây được xác định:
  • sel
  • we
  • addr[7:0]
  • wdata[15:0]
  • rdata[15:0]
Bên cạnh đó, JTAG controller và memory controller hoạt động trên hai miền clock bất đồng bộ với nhau nên cần phải áp dụng một cơ chế đồng bộ các tín hiệu giữa hai miền clock. Cơ chế được chọn là cơ chế bắt tay 4 pha. Cơ chế này như sau:
  1. Nguồn tích cực req để gửi một yêu cầu
  2. Đích tích cực ack để thông báo đã nhận yêu cầu
  3. Nguồn thôi tích cực req
  4. Đích thôi tích cực ack. Lúc này, nguồn có thể tích cực req để gửi yêu cầu mới
Hình 10: Cơ chế bắt tay 4 pha
Trong thiết kế này, cơ chế bắt tay 4 pha được thực hiện theo một cách khác:
  1. sel tích cực yêu cầu memory controller truy xuất bộ nhớ
  2. ready thôi tích cực thể hiện memory controller đang hoạt động
  3. ready tích cực khi đã hoàn thành truy xuất bộ nhớ
  4. sel thôi tích cực trước khi gửi yêu cầu tiếp theo
Hình 11: Cơ chế bắt tay đồng bộ giữa JTAG controller và memory controller
Như vậy, các tín hiệu giao tiếp cần thiết giữa JTAG controller và memory controller đã được xác định.
5) Phân tích tập lệnh JTAG
5.1) Tập lệnh
Để thực hiện được việc truy xuất bộ nhớ, thiết kế cần hỗ trợ thêm các lệnh mới, các lệnh không thuộc tập lệnh thông thường của JTAG. thiết kế này sẽ hỗ trợ các lệnh sau:
Bảng 2: Tập lệnh JTAG
TT
Tên
Opcode
Mô tả
1
BYPASS
4’b1111
Nối TDI đến TDO thông qua 1 Flip-Flop dịch
2
SET_ADDR
4’b1110
Thiết lập địa chỉ cần truy xuất
3
SET_DATA
4’b1100
Thiết lập yêu cầu đọc và đọc dữ liệu.
Thiết lập yêu cầu ghi và dữ liệu ghi cần ghi.
Kiểm tra trạng thái truy xuất bộ nhớ để biết một quá trình đọc/ghi đã hoàn thành hay chưa.

Thanh ghi IR sẽ gồm 4 bit để lưu các mã lệnh. Nếu chỉ hỗ trợ 3 lệnh như đã trình bày thì mã lệnh có thể là 2 bit và thanh ghi IR cũng chỉ có 2 bit.
Quá trình điều khiển thanh ghi lệnh, là quá trình thực thi các trạng thái chứa từ khóa “_IR” sẽ thực hiện việc dịch 4 bit mã lệnh vào IR thông qua tdi và dịch một giá trị cố định là 4’b0101 ra tdo.
Hình 12: Quá trình nạp lệnh vào IR
5.2) Thanh ghi dịch dữ liệu 
Thanh ghi dịch dữ liệu DR sẽ là một thanh ghi có tối thiểu 16 bit để có thể chứa được: 
  • 1 bit trong lệnh BYPASS 
  • 8 bit địa chỉ trong lệnh SET_ADDR 
  • 16 bit dữ liệu đọc/ghi trong lệnh SET_DATA 
Trong thiết kế này, ngoài 16 bit tối thiểu, thanh ghi dịch DR còn có thêm 2 bit để lưu giá trị điều khiển đọc/ghi/kiểm tra hoạt động của memory controller.
Như đã trình bày ở mục trên, trong lệnh SET_DATA, thông qua JTAG, người dùng có thể đọc bộ nhớ, ghi bộ nhớ hoặc kiểm tra trạng thái hoạt động. Ba hoạt động này cần 2 bit để phân biệt, cộng thêm số lượng bit lớn nhất để chứa các dữ liệu cần thiết là 16 bit. Như vậy, thanh ghi dịch DR sẽ có 18 bit.
Bảng 3: Bảng mã hoạt động của lệnh SET_DATA
TT
Tên
Opcode
Mô tả
1
CHECK
2’b00
Kiểm tra trạng thái truy xuất bộ nhớ hiện tại
BUSY = 2’b01
OKAY = 2’b10
2
READ
2’b01
Đọc bộ nhớ theo địa chỉ đã được thiết lập bởi lệnh SET_ADDR trước đó
3
WRITE
2’b10
Ghi bộ nhớ theo địa chỉ đã được thiết lập bởi lệnh SET_ADDR trước đó.
Giá trị ghi là các bit [17:2] của thanh ghi DR.

Hình sau đây thể hiện các bit của thanh ghi dịch DR được sử dụng và ý nghĩa của chúng trong từng lệnh. 
  • Lệnh BYPASS chỉ dùng bit 17 
  • Lệnh SET_ADDR chỉ dùng bit [17:10] 
  • Lệnh SET_DATA dùng toàn bộ thanh ghi dịch DR trong đó: 
    • Dữ liệu dịch vào trên tdi có ý nghĩa như sau: 
      • [17:2] là dữ liệu ghi nếu [1:0]=2’b10, ứng với yêu cầu WRITE. Đối với các mã yêu cầu khác như CHECK READ, trường này có thể là giá trị bất kỳ 
      • [1:0] là mã yêu cầu CHECK, READ hoặc WRITE 
    • Dữ liệu dịch ra trên tdo có ý nghĩa như sau: 
      • Nếu mã yêu cầu là CHECK 
        • [17:2] là dữ liệu đọc/ghi của yêu cầu gần nhất nếu [1:0]=2’b10, ứng với đáp ứng OKAY 
        • [17:2] là một giá trị bất kỳ nếu [1:0]=2’b01, ứng với đáp ứng BUSY. Lúc này, memory controller đang truy xuất bộ nhớ. 
      • Nếu mã yêu cầu là READ hoặc WRITE 
        • [17:2] là giá trị bất kỳ, không có ý nghĩa 
        • [1:0] sẽ cho biết mã yêu cầu hiện tại có được thực thi hay không. 
          • Nếu là OKAY, READ/WRITE sẽ được thực thi.
          • Nếu là BUSY, READ/WRITE sẽ có thể được thực thi hoặc không.
Hình 13: Vị trí và ý nghĩa các bit DR được sử dụng trong từng lệnh
6) Hoạt động các lệnh
Hoạt động của các lệnh được thực thi ở trạng thái RUN_TEST_IDLE hoặc quá trình điều khiển thanh ghi dữ liệu DR, các trạng thái chứa từ khóa “_DR”.
6.1) Lệnh BYPASS 
tdi được kết nối đến tdo thông qua một Flip-Flop. Chú ý, tdo được lái theo cạnh xuống tck nên giữa tdi tdo là 2 FF. Một FF hoạt động theo cạnh lên để lấy mẫu giá trị trên tdi, một FF hoạt động theo cạnh xuống để lái giá trị đã lấy mẫu đến tdo.
Hình 14: Kết nối giữa tdi tdo trong lệnh BYPASS
6.2) Lệnh SET_ADDR 
Thực thi hai hoạt động sau: 
  • Nạp 8 bit địa chỉ từ tdi vào bit [17:10] của thanh ghi dịch DR
  • Dịch 8 bit địa chỉ trước đó ra tdo
Hình 15: Hoạt động của lệnh SET_ADDR
6.3) Lệnh SET_DATA với mã yêu cầu WRITE
Thực thi hai hoạt động sau: 
  • Nạp 16 bit dữ liệu và 2 bit mã yêu cầu từ tdi vào thanh ghi dịch DR 
  • Dịch 18 bit ra tdo nhưng chỉ bit [1:0] là có ý nghĩa. Giá trị 2 bit này cho biết yêu cầu có được thực thi hay không. 
Kết quả: JTAG controller gửi một yêu cầu ghi vào bộ nhớ đến memory controller: 
  • sel=1 
  • we=1 
  • addr[7:0] được thiết lập từ lệnh SET_ADDR
  • wdata[15:0]=DR[17:2]
Hình 16: Hoạt động của lệnh SET_DATA với mã yêu cầu WRITE
6.4) Lệnh SET_DATA với mã yêu cầu READ 
Thực thi hai hoạt động sau: 
  • Nạp 2 bit mã yêu cầu từ tdi vào bit [1:0] của thanh ghi dịch DR. Chú ý, phải dịch đầy đủ 18 bit nhưng chỉ 2 bit LSB là có ý nghĩa. 
  • Dịch 18 bit ra tdo nhưng chỉ bit [1:0] là có ý nghĩa. Giá trị 2 bit này cho biết yêu cầu có được thực thi hay không. 
Kết quả: JTAG controller gửi một yêu cầu đọc bộ nhớ đến memory controller: 
  • sel=1 
  • we=0
  • addr[7:0] được thiết lập từ lệnh SET_ADDR
Hình 17: Hoạt động của lệnh SET_DATA với mã yêu cầu READ
6.5) Lệnh SET_DATA với mã yêu cầu CHECK 
Thực thi hai hoạt động sau: 
  • Nạp 2 bit mã yêu cầu từ tdi vào bit [1:0] của thanh ghi dịch DR. Chú ý, phải dịch đầy đủ 18 bit nhưng chỉ 2 bit LSB là có ý nghĩa. 
  • Dịch 18 bit ra tdo
    • Nếu bit [1:0]=2’b01, ứng với OKAY, thì [17:2] là dữ liệu đọc/ghi của yêu cầu READ/WRITE gần nhất. 
    • Nếu bit [1:0]=2’b10, ứng với BUSY, thì [17:2] không có ý nghĩa. 
Kết quả: JTAG controller giám sát trạng thái tín hiệu ready của memory controller: 
  • ready=1 trả về đáp ứng OKAY kèm dữ liệu đọc trên rdata[15:0] hoặc dữ liệu ghi của lệnh ghi trước đó
  • ready=0 trả về đáp ứng BUSY
Hình 18: Hoạt động của lệnh SET_DATA với mã yêu cầu CHECK

6.6) Lưu ý quan trọng 

Phải sử dụng yêu cầu CHECK để đảm bảo trạng thái của memory controller là OKAY trước khi phát yêu cầu READ hoặc WRITE vì nếu phát một yêu cầu READ/WRITE trong khi trạng thái là BUSY thì READ/WRITE không đảm bảo sẽ được thực hiện, có thể thực hiện hoặc không.
Không sử dụng bất kỳ mã lệnh (opcode) hoặc mã yêu cầu nào khác các giá trị đã mô tả. JTAG sẽ không đảm bảo hoạt động chính xác với các mã không hỗ trợ.
7) Cơ chế điều khiển bộ nhớ 
Để phân tích cơ chế này, chúng ta căn cứ trên giản đồ định thời của bộ nhớ trên chip. Giao tiếp của bộ nhớ này là loại giao tiếp bất đồng bộ, không hoạt động theo clock. Bộ điều khiển bộ nhớ cần duy trì các tín hiệu điều khiển trong khoảng thời gian yêu cầu để có thẻ đọc hoặc ghi bộ nhớ. 
Đối với quá trình ghi, các tín hiệu điều khiển phải giữ bằng hoặc lớn hơn Twrite
Đối với quá trình đọc, các tín hiệu điều khiển phải duy trì trong khoảng thời gian lớn hơn Ttrans. Như vậy, người thiết kế cần duy trì một khoảng thời gian, gọi là Tstable để lấy mẫu dữ liệu đọc từ bộ nhớ. 
Khoảng thời gian ghi (Twrite) và đọc (Ttrans+Tstable) có thể giống hoặc khác nhau. Một bộ đếm chạy theo clock clk được sử dụng để giám sát độ rộng của các tín hiệu điều khiển. Như vậy, sau khi nhận yêu cầu đọc/ghi từ JTAG controller, memory controller sẽ lái các tín hiệu điều khiển bộ nhớ theo clock clk và kích hoạt bộ đếm giám sát độ rộng tín hiệu. Tùy vào quá trình truy xuất bộ nhớ là ghi hay đọc mà giá trị bộ đếm sẽ so sánh với giá trị Twrite hoặc Ttrans+Tstable để báo kết thúc một lần truy xuất.
Trong thiết kế này, giá trị so sánh được sử dụng sẽ là giá trị lớn nhất giữa Twrite Ttrans+Tstable. Cách này được sử dụng nếu hai giá trị này không chênh lệnh quá lớn hoặc tốc độ truy xuất bộ nhớ không cần nhanh nhất có thể, nó giúp giảm tài nguyên mạch so sánh giá trị bộ đếm. Cách này không nên sử dụng nếu thời gian đọc và ghi bộ nhớ chênh lệch quá lớn hoặc tốc độ truy xuất bộ nhớ được yêu cầu càng nhanh càng tốt.
Hình 19: Phân tích định thời các tín hiệu điều khiển bộ nhớ
8) Phân tích thiết kế khối JTAG controller 
8.1) TAP FSM 
TAP FSM tuân theo chuẩn IEEE 1149.1 và được reset bất đồng bộ bởi tín hiệu jtag_rst_n. FSM có hai quá trình gọi là: 
  • Quá trình điều khiển IR
  • Quá trình điều khiển DR
Hình 20: TAP FSM
FSM sẽ tạo ra các tín hiệu điều khiển các thanh ghi dựa trên trạng thái hoạt động. Các tín hiệu này gồm:
  • cir_state: Tích cực trong trạng thái CAPTURE_IR
  • sir_state: Tích cực trong trạng thái SHIFT_IR
  • uir_state: Tích cực trong trạng thái UPDATE_IR
  • cdr_state: Tích cực trong trạng thái CAPTURE_DR
  • sdr_state: Tích cực trong trạng thái SHIFT_DR
  • udr_state: Tích cực trong trạng thái UPDATE_DR
  • select_ir: Tích cực trong trạng thái “_IR”, RUN_TEST_IDLE và TEST_LOGIC_RESET
Hình 21: Các tín hiệu giao tiếp của FSM
Một số ngõ ra của FSM sẽ được tổ hợp và chốt lại theo cạnh xuống của tck, ứng với cạnh lên của clock tck_n.
Hình 22: Mạch nguyên lý các tín hiệu điều khiển được tạo ra từ ngõ ra FSM
8.2) Thanh ghi IR
Thanh ghi lệnh sẽ gồm một thanh ghi dịch, shift_ir_reg[3:0], và một thanh ghi lưu lại mã lệnh, ir_reg[3:0]. Mỗi thanh ghi có 4 bit.
Hình 23: Mạch nguyên lý thanh ghi IR
Lệnh lưu trong ir_reg[3:0] sẽ được giải mã bằng cách so sánh với mã lệnh tương ứng để tích cực tín hiệu báo mã lệnh hiện tại.
Hình 24: Mạch nguyên lý giải mã lệnh
8.3) Ngõ ra tdo
Ngõ ra dữ liệu nối tiếp tdo sẽ được kết nối đến vị trí bit phù hợp tùy thuộc vào quá trình hiện tại và lệnh hiện tại.
  • Nếu là quá trình điều khiển IR thì tdo nối đến bit 0 của thanh ghi dịch IR
  • Nếu là quá trình điều khiển DR thì tdo nối đến một vị trí bit của thanh ghi dịch DR tùy vào mã lệnh hiện tại.
Hình 25: Mạch nguyên lý ngõ ra tdo, logic tổ hợp tdo và enable_tdo để tạo ra TDO thuộc TOP module
8.4) Thanh ghi dịch DR
Trong quá trình điều khiển DR, thanh ghi dịch DR, shift_dr_reg[17:0], được kết nối giữa tdi và tdo để nhận dữ liệu nối tiếp từ tdi và dịch dữ liệu được quy định bởi thiết kế JTAG ra tdo.
Hình 26: Mạch nguyên lý thanh ghi dịch DR
Tùy vào lệnh hiện tại, dữ liệu được truyền ra tdo sẽ khác nhau căn cứ trên đặc tả từng lệnh đã được mô tả ở các mục trước. Cụ thể: 
  • Nếu lệnh hiện tại là SET_ADDR, inst_setAddr=1, thì dữ liệu được dịch ra trên tdo lấy từ thanh ghi addr_reg[7:0]. Thanh ghi này đang lưu giá trị địa chỉ của lần truy xuất gần nhất. 
  • Nếu lệnh hiện tại là SET_DATA, inst_setData=1, thì tùy vào trạng thái của khối mem_ctrl mà hai bit LSB sẽ là BUSY hoặc OKAY.
  • Nếu mem_ctrl đang rảnh (sau khi đã hoàn thành một quá trình truy xuất bộ nhớ), mem_ctrl_idle=1, thì tùy vào quá trình vừa thực hiện là READ hoặc WRITE mà dữ liệu được dịch ra trên tdo là dữ liệu đọc từ bộ nhớ, rdata_reg[15:0], hoặc từ thanh ghi lưu dữ liệu ghi gần nhất, wdata[15:0].
Hình 27: Mạch nguyên lý lựa chọn dữ liệu để nạp cho thanh ghi dịch DR
8.5) Cờ xác định yêu cầu hiện tại
Một thanh ghi 1 bit, read_process, được sử dụng để xác định yêu cầu hiện tại là READ hay WRITE/CHECK. Thanh ghi này cập nhật giá trị sau mỗi quá trình điều khiển DR, trong trạng thái UPDATE_DR.
  • Nếu read_process=0, quá trình hiện tại là WRITE hoặc CHECK
  • Nếu read_process=1, quá trình hiện tại là READ
Hình 28: Mạch nguyên lý của cờ báo yêu cầu hiện tại
8.6) Ngõ ra yêu cầu mem_ctrl truy cập bộ nhớ
Ngõ ra sel we chỉ tích cực khi có một yêu cầu READ hoặc WRITE trong khi mem_ctrl đang rảnh, ready=1. Yêu cầu phải bị xóa, sel=0, khi phát hiện sự chuyển trạng thái từ BUSY sang OKAY của mem_ctrl, ứng với tín hiệu ready chuyển từ 0 sang 1.
Hình 29: Mạch nguyên lý của sel và we
8.7) Mạch đồng bộ
mem_ctrl hoạt động theo clock clk trong khi jtag_ctrl hoạt động theo clock tck. clk tck là hai clock bất đồng bộ. Vì vậy, các tín hiệu gửi từ mem_ctrl, miền clock clk phải được đồng bộ với miền clock tck
Trong thiết kế này, ready rdata[15:0] là hai tín hiệu được gửi từ mem_ctrl đến jtag_ctrl nhưng chỉ tín hiệu ready được đồng bộ. Sau khi đồng bộ, một mạch phát hiện cạnh lên ready sẽ được dùng để tạo ra tín hiệu cho phép, capture_rdata, lưu lại giá trị rdata[15:0]. Đồng thời, tín hiệu này cũng được dùng để xóa sel về mức 0, kết thúc một yêu cầu READ hoặc WRITE.
rdata[15:0] không cần đồng bộ vì thời điểm capture_data tích cực, nó đã có giá trị ổn định.
Hình 30: Mạch đồng bộ tín hiệu ready

9) Phân tích thiết kế khối mem_ctrl 

9.1) Mạch đồng bộ
Các tín hiệu sel, we, addr wdata được tạo từ clock tck, bất đồng bộ với clock clk của mem_ctrl, nên chúng cần được đồng bộ với clock clk trước khi sử dụng. Trong đó, tín hiệu sel quyết định tính hợp lệ của các tín hiệu khác nên trong thiết kế này, chỉ tín hiệu sel được đồng bộ. Một mạch phát hiện cạnh lên của sel được đặt sau mạch đồng bộ của sel để tạo ra tín hiệu lấy mẫu các giá trị của we, addrwdata.
Hình 31: Mạch đồng bộ tín hiệu sel và lấy mẫu các tín hiệu điều khiển
9.2) Máy trạng thái điều khiển quá trình truy xuất bộ nhớ 
Một FSM được sử dụng để quản lý việc truy xuất bộ nhớ. FSM chỉ có hai trạng thái: 
  • MEM_IDLE: mem_ctrl đang rảnh và có thể nhận yêu cầu truy xuất mới từ jtag_ctrl. Lúc này ready=1
  • MEM_ACCESS: mem_ctrl tích cực tín hiệu mem_sel để bắt đầu quá trình truy xuất bộ nhớ.
Hình 32: FSM và tín hiệu điều khiển từ FSM của mem_ctrl
9.3) Bộ đếm giám sát thời gian truy xuất bộ nhớ 
Một bộ đếm được sử dụng để giám sát thời gian truy xuất bộ nhớ. Thời gian này được tính như sau: 
Thời gian truy xuất = Tclk*(ACC_DELAY+1)
Trong đó, ACC_DELAY là một hằng số cố định. Tùy vào hệ thống mà giá trị ACC_DELAY sẽ được chọn phù hợp.
Hình 33: Bộ đếm giám sát thời gian truy xuất bộ nhớ
9.4) Thanh ghi dữ liệu đọc
Dữ liệu đọc từ bộ nhớ sẽ được lưu lại trong một thanh ghi ở cuối trạng thái MEM_ACCESS.
Hình 34: Thanh ghi lưu dữ liệu đọc
10) Mô hình bộ nhớ 
Để mô phỏng thiết kế bộ nhớ sẽ được mô hình hóa như sau: 
  • clockGen: Một bộ tạo clock nội để định thời cho quá trình đọc/ghi 
  • timingCounter: Bộ đếm giám sát thời gian Twrite Ttrans 
  • Write decoder: Giải mã địa chỉ ghi 
  • Read decoder: Giải mã địa chỉ đọc
  • memoryArray: Mảng bộ nhớ có dung lượng 256x16
Hình 35: Cấu trúc mô hình bộ nhớ mem_model
11) Tóm tắt
Bài viết trình bày chi tiết cách thực hiện đọc/ghi một bộ nhớ thông qua giao thức JTAG. Đây là một cách thực hiện đơn giản giúp bạn đọc hiểu làm thế nào giao thức JTAG có thể được tích hợp chức năng programming.
RTL code chi tiết kèm testbench đơn giản có thể được tải bởi link cuối bài viết. Để hiểu rõ hơn hoạt động của chức năng programming trong bài viết này. Tác giả sẽ phân tích chi tiết waveform của quá trình ghi và đọc bộ nhớ thông qua JTAG trong bài viết tiếp theo.

Dữ liệu có thể tải:

Lịch sử cập nhật:
1) 2019.11.15 - Tạo lần đầu
2) 2019.11.20 - Cập nhật hình 27, Mạch nguyên lý lựa chọn dữ liệu để nạp cho thanh ghi dịch DR

0 bình luận:

Đăng nhận xét