---
# System prepended metadata

title: UART-SPI READER 實現

---

# UART-SPI READER 實現
組員: A1105107 莊庭勛
指導老師:林宏益
# 一、作業內容
![image](https://hackmd.io/_uploads/BJ3jMF3wkl.png)
透過UART從電腦傳輸訊號到FPGA板子後，輸入特定的指令讀出NOR-Flash地址裡面的數值

# 二、實驗材料
- NOR-flash
- FPGA板
- 麵包版
- 杜邦線
- RS232
- 七段顯示器
# 三、實驗步驟
需要以下的module
- UART - receiver
- SPI controller
## UART - receiver 
### A. UART - receiver 原始(接收1個byte)
首先要透過AccessPort傳送訊號到FPGA，需要用verilog設計一個UART接收器，
![image](https://hackmd.io/_uploads/HyNXBMMFp.png)
要利用UART通訊協議來實現資料傳輸
以下是UART receiver的top module:
```verilog=
//TOP MODULE
`timescale 1ns / 1ps

module rx_top(
    input clk,
    input rst,
    input rx_pin_in,
    output [7:0] seg7
    );
    wire [7:0] rx_data;  
    wire rx_band_sig;
    wire clk_bps;
    
    rx_band_gen rx_band_gen(
        .clk( clk ),
        .rst( rst ),
        .band_sig( rx_band_sig ),
        .clk_bps( clk_bps )
    );
    H2L_detect rx_in_detect(
        .clk( clk ),
        .rst( rst ),
        .pin_in( rx_pin_in ),
        .sig_H2L( rx_pin_H2L )
    );
    rx_ctl rx_ctl(
        .clk( clk ),
        .rst( rst ),
        .rx_pin_in( rx_pin_in ),
        .rx_band_sig( rx_band_sig ),
        .rx_clk_bps( clk_bps ),
        .rx_data( rx_data ),
        .rx_done_sig( rx_done_sig )
    );
    
    ASCII27 ASCII27(
        .rst( rx_done_sig ),
        .ascii( rx_data ), //rx_data訊號->ASCII
        .seg7(seg7)
    );
endmodule
```
UART receiver主要分成幾個結構:
#### 1. rx_band_gen 
因為在FPGA板子的工作系統頻率是125M Hz，但是我們使用的Baud rate是**9600bps**去接收資料，所以要做除頻的動作
```verilog=
module rx_band_gen(
    input clk,
    input rst,
    input band_sig,
    output reg clk_bps
    );
    parameter SYS_RATE = 125000000;//FPGA 之系統頻率
    parameter BAND_RATE = 9600;
    parameter CNT_BAND = SYS_RATE / BAND_RATE;
    parameter HALF_CNT_BAND = CNT_BAND / 2;
    reg [13:0]cnt_bps;
    always @( posedge clk or posedge rst )
    if( rst )
        begin
            cnt_bps <= HALF_CNT_BAND;
            clk_bps <= 1'b0;
        end
    else if( !band_sig )
        begin
            cnt_bps <= HALF_CNT_BAND;
            clk_bps <= 1'b0;
        end
    else if( cnt_bps == CNT_BAND )
        begin
            cnt_bps <= 14'd0;
            clk_bps <= 1'b1;
        end
    else
        begin
            cnt_bps <= cnt_bps + 1'b1;
            clk_bps <= 1'b0;
        end
endmodule
```
#### 2. H2L_detect
此模組的目的是為了要去接收start的資料，當接收到start的訊號，rx_ctl的狀態機會進行切換，告訴系統要做接收的動作
```verilog=
module H2L_detect(
    input clk,
    input rst,
    input pin_in,
    output sig_H2L
    );
    reg pin_pre;
    assign sig_H2L = !pin_in & pin_pre;
    always @( posedge clk or posedge rst )
    if( rst )
        pin_pre <= 1'b0;
    else
        pin_pre <= pin_in;
endmodule
```
H2L的原理如下圖所示
![image](https://hackmd.io/_uploads/S1VsoXGY6.png)
先把`pin_in`延後一個週期得到`pin_pre`，則可以發現
再將`!pin_in` 和 `pin_pre`做and即可得到`sig_H2L`
#### 3. UART控制器及狀態機
控制器要去控制整個系統的dataflow和狀態切換
這邊的接收器直接使用張家凱同學的控制器:
```verilog=
module rx_ctl(
    input clk,
    input rst,
    input rx_pin_in,
    input rx_pin_H2L,
    output reg rx_band_sig,
    input rx_clk_bps,
    output reg [7:0] rx_data,
    output reg rx_done_sig
);
    

localparam [4:0]
    IDLE = 5'b00001,
    BEGIN = 5'b00010,
    DATA = 5'b00100, 
    END = 5'b01000,
    BFREE = 5'b10000;

reg [4:0] pos; 
reg [2:0] data_bit_count; // 計數器用於追蹤數據位

always @(posedge clk or posedge rst) begin
    if (rst) begin
        rx_band_sig <= 1'b0;
        rx_data <= 8'd0;
        pos <= IDLE;
        rx_done_sig <= 1'b0;
        data_bit_count <= 3'd0;
    end else begin
        case (pos) // 使用one-hot狀態機
            IDLE: begin
                if (rx_pin_H2L) begin
                    rx_band_sig <= 1'b1;
                    pos <= BEGIN;
                    rx_data <= 8'd0;
                    data_bit_count <= 3'd0;
                end
            end
            BEGIN: begin
                if (rx_clk_bps) begin
                    if (rx_pin_in == 1'b0) begin
                        pos <= DATA;
                    end else begin
                        rx_band_sig <= 1'b0;
                        pos <= IDLE;
                    end
                end
            end
            DATA: begin
            if (rx_clk_bps) begin
                rx_data[data_bit_count] <= rx_pin_in;
                if (data_bit_count == 3'd7) begin
                    pos <= END;
                end else begin
                    data_bit_count <= data_bit_count + 1'b1;
                end
            end
        end
        END: begin
            if (rx_clk_bps) begin
                rx_done_sig <= 1'b1;
                pos <= BFREE;
                rx_band_sig <= 1'b0;
            end
        end
        BFREE: begin
            if (rx_clk_bps) begin
                rx_done_sig <= 1'b0;
                pos <= IDLE;
            end
        end
    endcase
   end
end

endmodule
```
當在`IDLE`時候:
**系統等待H2L_detect模組偵測start(`rx_pin_H2L`)訊號**
當偵測到start後，啟動`rx_band_sig`，系統開始進行接收訊號進入`BEGIN`

當在`BEGIN`時候:
**要去檢測起始位是否正確**
在rx_clk_bps到達時
- 若rx_pin_in 是 0，表示起始位有效，進入`DATA`
- 若rx_pin_in 是 1，表示起始位無效，回到`IDLE`

當在`DATA`時候:
**會按照Baud rate(`rx_clk_bps`)逐漸接收數據**
當`rx_clk_bps`抵達時，將`rx_pin_in`的資料存入`rx_data`，而是`rx_data`的第幾位是由`data_bit_count`決定，當`data_bit_count`接受8個數據接收完畢時，進入`END`狀態。
否則`data_bit_count`逐漸加一接收下一位元

當在`END`時候:
**要去結束數據接收並且發送接收完成的訊號**
`rx_clk_bps`時，將傳送結束訊號(`rx_done_sig`)、停止接收過程(`rx_band_sig`)，清除`rx_band_sig`，並且將狀態切換到`BFREE`

當在`BFREE`時:
**確保`rx_done_sig`只維持一個週期**
將`rx_done_sig`清零，並回到`IDLE`
#### 4. ASCII轉換seg7
將接受到的訊號`rx_data`轉換成seg7，方可在FPGA板子上面顯示
```verilog=
module ASCII2seg7 (
    rst,
    ASCII,
    seg7_out
    );

input rst;
input [7:0] ASCII;
reg [7:0] seg7;
output [7:0] seg7_out;
always@( posedge rst )
begin

    case (ASCII)
        8'b0011_0000:
            seg7<=8'b00000011;
        8'b0011_0001:
            seg7<=8'b10011111;
        8'b0011_0010:
            seg7<=8'b00100101;
        8'b0011_0011:
            seg7<=8'b00001101;
        8'b0011_0100:
            seg7<=8'b10011001;
        8'b0011_0101:
            seg7<=8'b01001001;
        8'b0011_0110:
            seg7<=8'b01000001;
        8'b0011_0111:
            seg7<=8'b00011111;
        8'b0011_1000:
            seg7<=8'b00000001;
        8'b0011_1001:
            seg7=8'b00001001;
        8'b0100_0001:
            seg7=8'b00010001;
        8'b0100_0010:
            seg7=8'b11000001;
        8'b0100_0011:
            seg7=8'b11100101;
        8'b0100_0100:
            seg7=8'b10000101;
        8'b0100_0101:
            seg7=8'b01100001;
        8'b0100_0110:
            seg7=8'b01110001;
        default:
            seg7=8'b01100001;
    endcase

end
assign seg7_out=seg7;
endmodule
```
![image](https://hackmd.io/_uploads/rJJt0XzFa.png)

### B.UART - receiver (接收2個byte)
因為在使用上我們需要輸入R0~RF 2個Byte的資訊，所以我們需要在UART - receiver做一些修改。
```verilog =
`timescale 1ns / 1ps
module rx_ctl (
    input clk,
    input rst,
    input rx_pin_in,
    input rx_pin_H2L,
    output reg rx_band_sig,
    input rx_clk_bps,
    output reg [15:0] out_data,
    output reg rx_done_sig,
    output  seg
);

   localparam [5:0] IDLE = 6'b000001, 
   BEGIN = 6'b000010, 
   DATA= 6'b000100, 
   END = 6'b001000, 
   BFREE = 6'b010000,
   FINISH = 6'b100000;
    reg [5:0] pos;
            
    reg [7:0] rx_data;
    reg [2:0] data_bit_count; 
    reg [1:0] count;
    reg [15:0] out_data1;

    always @(posedge clk or posedge rst)
        if (rst) begin
            rx_band_sig <= 1'b0;
            rx_data <= 8'd0;
            pos <= IDLE;
            data_bit_count<=3'd0;
            rx_done_sig <= 1'b0;
            count <= 2'd0;
        end else begin
            case (pos)
                IDLE:
                    if (rx_pin_H2L) begin
                        rx_done_sig <= 1'b0;
                        rx_band_sig <= 1'b1;
                        pos <= BEGIN;
                        rx_data <= 8'd0;
                        data_bit_count<=3'd0;
                    end
                BEGIN:
                    if (rx_clk_bps) begin
                        if (rx_pin_in == 1'b0) begin
                            pos <= DATA;
                        end else begin
                            rx_band_sig <= 1'b0;
                            pos <= IDLE;
                        end
                    end
                DATA: begin
            if (rx_clk_bps) begin
                rx_data[data_bit_count] <= rx_pin_in;
                if (data_bit_count == 3'd7) begin
                    pos <= END;
                end else begin
                    data_bit_count <= data_bit_count + 1'b1;
                end
            end
        end
                END:
                    if (rx_clk_bps) begin
                        rx_band_sig <= 1'b0;
                        pos <= BFREE;                     
                      if (count == 2'd0) begin
                        out_data1 <= {rx_data,out_data1[7:0]};
                      end
                      else if (count == 2'd1) begin
                        out_data1 <= {out_data1[15:8],rx_data};
                      end
                    end
                BFREE:
                    begin
                        if (out_data1[15:8] != 8'h52 && count == 2'd0 ) begin
                                rx_done_sig <= 1'b0;
                                pos <= IDLE;
                                count <= 2'd0;
                        end
                        else  begin
                                rx_done_sig <= 1'b0;
                                pos <= FINISH;
                                count <= count + 2'd1;
                        end
                    end
                FINISH:
                    begin
                        if (count == 2'd2) begin
                            out_data <= out_data1;
                            count <= 2'd0;
                            rx_done_sig <= 1'b1;
                            out_data1 <= 16'd0;
                        end
                        pos <= IDLE;
                    end
            endcase
        end
endmodule
```
在原始的`rx_data`是8bit,要將其改成16 bit(因為要接收R0~RF的訊號)
主要的更改在`END`、`BFREE`和多加入`FINISH`狀態
:::info
在原本的`END`是要負責**去結束數據接收並且發送接收完成的訊號**
info rx_clk_bps時，將傳送結束訊號(rx_done_sig)、停止接收過程(rx_band_sig)，清除rx_band_sig，並且將狀態切換到BFREE 
:::
此時的`END`狀態要多負責將讀取後的資料將其放到`out_data`的register裡面，也多了一個`count`的register，此register要去紀錄目前輸入的是第幾個byte
- 當`count`為0，代表準備接受第一個byte，則將剛剛的`rx_data`接受到的值放到`out_data1`的前面8個bit，此時接收到的值要會是**R**
- 當`count`為1，代表準備接受第二個byte，則將剛剛的`rx_data`接受到的值放到`out_data1`的後面8個bit，此時接收到的值要會是**0~f**
:::info
在原本的`BFREE`是要負責**確保rx_done_sig只維持一個週期**
將rx_done_sig清零，並回到IDLE
:::
此時的`BFREE`狀態要檢查`out_data1`的第一個byte是不是為**R**
若不是則代表有錯誤，將`rx_done_sig`和`count`清零並且回到`IDLE`
反之若第一個byte是**R**，則代表使用者輸入的第一個byte是正確的，將其count加一並將狀態切到`FINISH`

`FINISH`要去檢查`count`是否為2，若是將`out_data1`的值做輸出，`rx_done_sig`和`count`清零表示完畢

---
之後再將剛剛的out_data送Cmd_Flash_Logic這一個module
將out_data轉換成address，方便給NOR_Flash做讀取
```verilog
`timescale 1ns / 1ps

module Cmd_Flash_Logic(

    input  wire       sys_clk,        
    input  wire       sys_rst_n,      
    

    input  wire       rx_done_sig,    
    input  wire [15:0] rx_data,        
    

    output reg         start_read,
    output reg  [23:0] addr_offset
);

    reg  [7:0] cmd_byte_1, cmd_byte_2;
    reg  [1:0] byte_count;  
    reg  [23:0]addr; 
    

   always @(*) begin
    case (cmd_byte_2[7:0])
     8'b0011_0000: addr=24'h000000;
     8'b0011_0001: addr=24'h000001;
     8'b0011_0010: addr=24'h000002;
     8'b0011_0011: addr=24'h000003;
     8'b0011_0100: addr=24'h000004;
     8'b0011_0101: addr=24'h000005;
     8'b0011_0110: addr=24'h000006;
     8'b0011_0111: addr=24'h000007;
     8'b0011_1000: addr=24'h000008;
     8'b0011_1001: addr=24'h000009;
     8'b0100_0001: addr=24'h00000A;
     8'b0100_0010: addr=24'h00000B;
     8'b0100_0011: addr=24'h00000C;
     8'b0100_0100: addr=24'h00000D;
     8'b0100_0101: addr=24'h00000E;
     8'b0100_0110: addr=24'h00000F;
     default: addr= 24'h00000E;
    endcase
   end


parameter [1:0]IDLE = 2'd0;
parameter [1:0]READ_ADDRESS = 2'd1;
parameter [1:0]FINISH = 2'd2;

    always @(posedge sys_clk or posedge sys_rst_n) begin
        if(sys_rst_n) begin
            byte_count        <= 2'd0;
            cmd_byte_1        <= 8'd0;
            cmd_byte_2        <= 8'd0;
            start_read        <= 1'd0;
        end
        else begin



            if(rx_done_sig) begin
                case(byte_count)
                    IDLE: begin
                        start_read <= 1'd0;
                        cmd_byte_1 <= rx_data[15:8]; 
                        byte_count <= 2'd1;
                    end
                    READ_ADDRESS: begin
                        cmd_byte_2 <= rx_data[7:0];
                        byte_count <= 2'd2;


                        if(cmd_byte_1 == 8'h52) begin  // 0x52='R'

                            if(cmd_byte_2 >= 8'h30 && cmd_byte_2 <= 8'h39) begin
                                addr_offset <= addr ; 
                            end

                            else if(cmd_byte_2 >= 8'h41 && cmd_byte_2 <= 8'h46) begin
                                addr_offset <= addr ; 
                            end
                            else begin

                                addr_offset <= 24'h00000E;
                            end
                       end
                    end
                    FINISH: begin
                        start_read <= 1'd1;
                        byte_count <= IDLE;
                    end
                    default: byte_count <= IDLE;
                endcase
            end
        end
    end

    
endmodule
```
將剛剛control中的`out_data`(在此模組叫做`rx_data`)用`cmd_byte_1`和`cmd_byte_2`去分割出**R**和**數字**的部分
在`READ_ADDRESS`的階段時，
先確認第一個byte是否為**R**，若不是則`addr_offset`顯示為24'h00000E，表示error
若是R，則確認第二個byte是否在0~f的值，如果是在將其正確的值做輸出，放到`addr_offset`

將全部做完後，在`FINISH`將state回到`IDLE`還有將`start_read`設定為1，告訴後續的SPI controller可以進行讀取的動作。

## SPI Controller
![image](https://hackmd.io/_uploads/H1QCt32Dye.png)

SPI要將剛剛讀取的資料`rx_data`透過`MOSI`傳到NOR Flash做溝通
再透過`MISO`將NOR Flash的資料回傳到FPGA板，再將其顯示到七段顯示器

### SPI_clock除頻
首先，因為SPI的工作頻率是在1M Hz，而FPGA的工作頻率是125M Hz，所以如同UART的部分，要對其做除頻。如下:
``` verilog
module SPI_clock (
    input clk,  
    input rst,            
    output reg spclk        
);

    parameter SYS_RATE = 125000000; 
    parameter BAND_RATE = 1000000; 
    parameter CNT_BAND = 125; 
    parameter HALF_CNT_BAND = 62;    

    reg [31:0] cnt_bps; 

    always @(posedge clk or posedge rst)
    begin
        if (rst) begin
            cnt_bps <= 32'd0;      
            spclk <= 1'b0;       
        end else if (cnt_bps == HALF_CNT_BAND - 1) begin
            cnt_bps <= 32'd0;      
            spclk <= ~spclk;   
        end else begin
            cnt_bps <= cnt_bps + 1'b1; 
        end
    end
endmodule
```

---
### SPI control
![image](https://hackmd.io/_uploads/rkK1Sa3Dyl.png)
首先在NOR_Flash的指令要發送一個0X03h的CMD，NOR Flash才知道要做Read
所以下面有定義一個`READ_CMD` = 8'h03的指令，因為`MOSI`是要傳入訊號給到NOR flash，所以在`CMD`的state時

```
CMD: MOSI <= READ_CMD[7 - bit_cnt];
            CMD2: MOSI <= READ_CMD[0];
```
此2行的目的就是為了將8'h03的CMD傳入NOR Flash
而接下來就是傳入address，也是透過`MOSI`傳入
``` 
ADDR: MOSI <= addr_offset[23 - bit_cnt];
                ADDR2: MOSI <= addr_offset[23];
```
而當`MOSI`全部傳送完畢後，state切到`READ`
從NOR Flash讀取資料，並透過`MISO`傳輸，並且將其紀錄在`read_reg`的register之中
```
 else if(current_state==READ) begin
            // bit_cnt=0 => 先收最高位(7)
            read_reg[7 - bit_cnt] <= MISO;
        end
```
傳送完畢後，state切到`DONE`
並將`read_reg`的資訊傳送到`data_out`做輸出

而`CS_n`則是按照規格去做制定，當`IDLE`以及`DONE`時，`CS_n`才會為1
規格如下圖所示
![image](https://hackmd.io/_uploads/SkmlYphwJl.png)

SPI control完整的code如下所示
```verilog
//====================================================================
//SPI_control_read_only
//    0x03  (CMD + ADDR + 1 Byte DATA)
//====================================================================
module SPI_control_read_only(
    input  wire        sys_clk,
    input  wire        sys_rst_n,    
    input  wire        start_read,
    input  wire [23:0]  addr_offset,
    input wire          spclk,

    input  wire        MISO,
    output reg         MOSI,
    output wire        SPI_CLK,
    output reg         CS_n,


    output reg  [7:0]  data_out
);

    localparam IDLE = 3'd0;
    localparam CMD  = 3'd1;  // 發送 0x03
    localparam CMD2  = 3'd2;  
    localparam ADDR = 3'd3;  // 發送 24-bit addr
    localparam ADDR2 = 3'd4;
    localparam READ = 3'd5;  // 讀 1 Byte
    localparam DONE = 3'd6;

    reg [2:0]  current_state, next_state;
    reg [5:0]  bit_cnt;// 
    reg [7:0]  read_reg;  
    localparam [7:0] READ_CMD = 8'h03;
   

    // 狀態轉移
    always @(posedge spclk or posedge sys_rst_n) begin
        if(sys_rst_n)
            current_state <= IDLE;
        else
            current_state <= next_state;
    end
    always @(*) begin
        next_state = current_state;
        case(current_state)
            IDLE: if(start_read) next_state = CMD;
            else next_state = IDLE;
            CMD : if(bit_cnt == 6'd7) next_state = CMD2;
            else next_state = CMD;
            CMD2 : if(bit_cnt == 6'd0) next_state = ADDR;
            else next_state = CMD2;
            ADDR: if(bit_cnt == 6'd23) next_state = ADDR2;
            else next_state = ADDR;
            ADDR2: if(bit_cnt == 6'd0) next_state = READ;
            else next_state = ADDR2;
            READ: if(bit_cnt == 6'd7) next_state = DONE;
            else next_state = READ
            DONE: next_state = IDLE;
        endcase
    end

    // bit_cnt
    always @(posedge spclk or posedge sys_rst_n) begin
            if(sys_rst_n) 
                bit_cnt <= 6'd0;
            else begin
                case(current_state)
                    CMD:  if(bit_cnt==6'd7)  bit_cnt<=0; else bit_cnt<= bit_cnt+1'b1;
                    ADDR: if(bit_cnt==6'd23) bit_cnt<=0; else bit_cnt<= bit_cnt+1'b1;
                    READ: if(bit_cnt==6'd7)  bit_cnt<=0; else bit_cnt<= bit_cnt+1'b1;
                    default: bit_cnt <= 0;
                endcase
            end
        end



    // MOSI
    always @(posedge spclk or posedge sys_rst_n) begin
        if(sys_rst_n) begin
            MOSI <= 1'b0;
        end
        else begin
            case(next_state)
                CMD:  MOSI <= READ_CMD[7 - bit_cnt];
                CMD2: MOSI <= READ_CMD[0];
                ADDR: MOSI <= addr_offset[23 - bit_cnt];
                ADDR2: MOSI <= addr_offset[23];
                default: MOSI <= 1'b0;
            endcase
        end
    end

    // 從 MISO 讀資料
    always @(posedge spclk or posedge sys_rst_n) begin
        if(sys_rst_n) begin
            read_reg <= 8'd0;
        end
        else if(current_state==READ) begin
            // bit_cnt=0 => 先收最高位(7)
            read_reg[7 - bit_cnt] <= MISO;
        end
    end

    // CS_n 控制
    always @(posedge spclk or posedge sys_rst_n) begin
        if(sys_rst_n) 
            CS_n <= 1'b1;
        else begin
            case(next_state)
                CMD  :    CS_n <= 1'b0;
                CMD2 :    CS_n <= 1'b0;
                ADDR :    CS_n <= 1'b0;
                ADDR2 :    CS_n <= 1'b0;
                READ :    CS_n <= 1'b0;
                default:               CS_n <= 1'b1;
            endcase
        end
    end

    // data_ready & data_out
    always @(posedge spclk or posedge sys_rst_n) begin
        if(sys_rst_n) begin
            data_out   <= 8'd0;
        end
        else begin
            if(current_state==DONE) begin
                data_out   <= read_reg;
            end
        end
    end

    assign SPI_CLK = spclk;
endmodule
```
# 四、實驗數據

## UART - receiver(接收8bit)
::: success
影片連結:
https://studio.youtube.com/video/8nWxnUkTYok/edit
:::


## UART - receiver (接收16bit)
![image](https://hackmd.io/_uploads/BJV3ca3w1g.png)
::: success
影片連結:
https://studio.youtube.com/video/kvPTIJ1mfKo/edit
:::

## SPI - controller 
![image](https://hackmd.io/_uploads/HJQyj6nvyl.png)


## UART-SPI READER 
::: success
影片連結:
https://studio.youtube.com/video/sVkDUDID5e8/edit
:::

# 五、心得
在這次的期末作業學習到UART和SPI的通訊協定，為了將傳送2 byte的資料，在前面UART的部分就花了許多時間下去改，最後還是透過和學長討論及參考才將UART的部分先行完成，再來就是處理SPI的部分，這個部分在剛開始寫的時候也是毫無頭緒，這個訊號什麼要升起來，什麼時候要掉下去，光是在處理狀態機的部分想了很久。

中間也需要看NOR flash的手冊才知道部分的指令是什麼，但是最難的是，知道這邊指令要用哪一個但是要如何將其實現並且這個指令出現在正確的時間才是最困難的。

這個部分也是有看過助教的simulation才慢慢推敲出來，即便看到模擬正確，但是在接上板子後錯的機會還是很大，大部分的問題都是來自哪個訊號慢來或是早來的原因。

總的來說這次作業也是花了一些時間下來做
額外學到的東西更多像是HackMD的使用和指令的運用，這篇報告也是花了蠻多時間才將其整理出來
# 六、參考資料

:::warning
- 學長HackMD
- SPI_introduction
- NOR Flash規格書
:::



