Try   HackMD

FPGA 第六組 Lab3 結報

文章網址

組員

姓名 學號
劉永勝 F94089032
蔡宗瑾 E14083137
李宇洋 E24099025

Problem 1 - RGB LED [影片連結]

Block Design

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

不同於上次實驗,這次是要以c code控制led燈的亮燈順序。不像verilog有平行處理的概念,c code是序項執行。因此分成以下3個步驟,依序執行,完成這此實作。

  1. Set LED color
    設定三個參數,R、G、B,分別代表該顏色的16進為數值。例如紫色的16進制為0x7f_1f_ff,則分別指定參數 R = 0x7f、G = 0x1f、B = 0xff

  2. Bright

    • 根據PWM的觀念,以256個clk cycle作為1個週期,控制R、G、B各別亮燈時間。則步驟一set color的參數就能作為亮燈時間的最大值。見下方程式說明,第一圈for loop表示一個顏色停留的時間,第二圈則表示PWM以256個cycle為一周期。
    • 其中較特別的是第3、6、9行,rgb_data會透過XGpio函數,寫到實體LED的port上。由於線寬為 3-bit,由高位元到低位元各別代表B、G、R的開關,紅燈表示 3'b001、綠燈表示 3'b010、藍燈表示 3'b100。藉由不斷開關R、G、B三個燈的,欺騙人眼的方式實現PWM效果。 (但之後發現用c code的arithmetic operator &| 會更好)
    ​​​​for (int Delay = 0; Delay < LED_DELAY; Delay++){ ​​​​ for (int count = 0; count < 256; count ++) { ​​​​ rgb_data = (count < R)? 1 : 0; ​​​​ XGpio_DiscreteWrite(&RGB_Gpio, 1, rgb_data); ​​​​ rgb_data = (count < G)? 2 : 0; ​​​​ XGpio_DiscreteWrite(&RGB_Gpio, 1, rgb_data); ​​​​ rgb_data = (count < B)? 4 : 0; ​​​​ XGpio_DiscreteWrite(&RGB_Gpio, 1, rgb_data); ​​​​ } ​​​​}
  3. Change color
    題目要求6個顏色的燈要依序閃爍,則使用的個counter,每閃完一個燈號則加一,對此參數取 mod6,則可再步驟一set color時控制顏色。

Problem 2 - Sorting

Algorithm

此專案為單純讓c code跑在processing system上,並沒有使用到AXI介面資料傳輸,因此沒有考慮sorting電路的寫法。我們採用quick sort演算法,quicksort是一種遞迴式的排序演算法,其pseudo code如下所示:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

PARTITION的功能即是將array最右端的值A[r]與array其他值做比較,並且將array分成兩部分,左半部比A[r]小,右半部比A[r]大。
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

實作結果

需先輸入要排序的數字總數(key numbers),再依序輸入每一筆數字(key)。

  • switch為0: acsending order
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • switch為1: descending order
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

Problem 3

Block Design

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

電路設計說明

Problem3-1

AXI interface

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

slv_reg0 -> operand1
slv_reg1 -> operand2
slv_reg2 -> operator
slv_reg3 -> inValid
slv_reg4 -> dataResponse
slv_reg5 -> outData
slv_reg6 -> outValid
slv_reg7 -> overflow

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

state 說明
NOP 閒置狀態,不進行任何操作,並等待inValid訊號。
ADD 進行加法運算,並且在處理完成後拉高calcDone。
SUB 進行減法運算,並且在處理完成後拉高calcDone。
MUL 進行乘法運算,並且在處理完成後拉高calcDone。
DONE 將outValid設為1,並等待PS發回dataResponse訊號 (確認已收到Output data) 後,回到NOP state等待下一次操作。
設計邏輯
  • PS端(main.c):
/*--------------------------INPUT--------------------------*/ printf("Please choose calculation mode(1: add, 2: sub, 3: mul):\n\r"); scanf("%d", &opeartor); while(opeartor > 3 || opeartor < 1){ ... } printf("%d\r\n", opeartor); ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 8, opeartor); // operand1 printf("Please input operand1 (Which is smaller than 127, and bigger -128):\n\r"); scanf("%d", &operand1); while(operand1 > 127 || operand1 < -128){ ... } printf("%d\r\n", operand1); ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 0, operand1); // operand2 printf("Please input operand2 (Which is smaller than 127, and bigger -128):\n\r"); scanf("%d", &operand2); while(operand2 > 127 || operand2 < -128){ ... } printf("%d\r\n", operand2); ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 4, operand2);

上方程式碼用於確認使用者的input是否在允許範圍內,並且將value寫入對應的AXI slave register存取的位置。

// inValid high ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 12, 1); // Wait until the calculation is done while(ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 24) == 0){ ... if(ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 24) == 1){ // inValid low ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 12, 0); } } if(ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 24) == 1){ // inValid low ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 12, 0); } printf("Calculation is done!\r\n");

上方程式碼會將Processor hang在while迴圈直到確認Arithmetic module已經做完運算(如果已經完成會發回outValid訊號)。

// ! Output switch (opeartor) { case 1: printf("Result of %d + %d: %d\n",operand1, operand2, (int8_t)ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 20)); printf("Overflow: %d\n", ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 28)); break; case 2: printf("Result of %d - %d: %d\n",operand1, operand2, (int8_t)ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 20)); printf("Overflow: %d\n", ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 28)); break; case 3: printf("Result of %d * %d: %d\n",operand1, operand2, (int8_t)ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 20)); printf("Overflow: %d\n", ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 28)); break; default: break; } // Told PL the data is received ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 16, 1); printf("====================================\n"); // Reset for(int i=0;i<9;i++){ printf("Reset slave register %d: %d\n",i,ARITHMETIC_mReadReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 4*i)); ARITHMETIC_mWriteReg(XPAR_ARITHMETIC_0_S00_AXI_BASEADDR, 4*i, 0); } printf("====================================\n");

最後會將從PL得到的Output結果print出來,告知PL已經收到data(將dataResponse對應的slave register拉高),並且將AXI slave register的data進行重置,進行下一次操作。

  • PL端(Arithmetic.v):
// Registers reg calcDone, overflow_reg; reg signed [7:0] operand1_reg, operand2_reg; reg signed [15:0] outData_reg; // The redudant bits are for overflow detection // Wires assign outData = outData_reg[7:0]; ... // --------------------- // // outData // // --------------------- // always @(posedge clk) begin case (state) `ADD: begin outData_reg <= operand1_reg + operand2_reg; calcDone <= 1'b1; end `SUB: begin outData_reg <= operand1_reg - operand2_reg; calcDone <= 1'b1; end `MUL: begin outData_reg <= operand1_reg * operand2_reg; calcDone <= 1'b1; end `DONE: begin outData_reg <= outData_reg; calcDone <= 1'b1; end default: begin outData_reg <= 32'd0; calcDone <= 1'b0; end endcase end

outData_reg會在運算的state將已經儲存好的operand進行對應的運算,並且在DONE時將output值維持住,直到PS回傳dataResponse的訊號後回到NOP

// Registers reg calcDone, overflow_reg; reg signed [15:0] outData_reg; // The redudant bits are for overflow detection // Wires assign overflow = overflow_reg; ... // --------------------- // // overflow // // --------------------- // always @(posedge clk) begin if(state == `DONE) begin if(outData_reg > 127 || outData_reg < -128) overflow_reg <= 1'b1; else overflow_reg <= 1'b0; end else overflow_reg <= 1'b0; end

outData_reg因為有預留好多餘的8-bit空間,所以可以透過其在DONE時的value來判斷是否為overflow。

Problem3-2

Algorithm

本問題採用insertion sort的演算法來進行電路撰寫,insertion sort的pseudo code如下所示:

AXI interface

slv_reg0 -> inData
slv_reg1 -> inEN
slv_reg2 -> outData

State Diagram

state 說明
INIT 等待PL端接收到PS傳來的input enable訊號,收到後即跳至LOAD
LOAD 將slv_reg0的輸入資料一筆一筆讀進sorting array A中
COMPARE 判斷while_condition,確認 j>=0 and A[j]>key此條件
SWAP 執行上方pseudo code的第6以及第7行
LAST 執行上方pseudo code的第8行
設計邏輯
  • PS端(main.c):
/*--------------------------INPUT--------------------------*/ printf("Please input 8 numbers:\n\r"); for(i = 0; i < 8; i++) { printf("Input No.%d key: \r\n", i+1); scanf("%d", &key); while(key > 15 || key < 0){ printf("%d\r\n", key); printf("Your input is out of range!\n\r"); printf("Input No.%d key: \r\n", i+1); scanf("%d", &key); } printf("%d\r\n", key); inData = inData + ((key & 0xf) << (4*i)); } SORTING_mWriteReg(XPAR_SORTING_0_S00_AXI_BASEADDR, 0, inData); SORTING_mWriteReg(XPAR_SORTING_0_S00_AXI_BASEADDR, 4, 1); outData = SORTING_mReadReg(XPAR_SORTING_0_S00_AXI_BASEADDR, 8); /*--------------------------OUTPUT--------------------------*/ printf("Sorted numbers are: \r\n"); for(i = 0; i < 8; i++) { output = ((outData >> (4*i)) & 0xf); printf("%d\r\n", output); }

上方程式碼的第13行透過for loop將所有輸入的8筆4-bit資料全部存入inData,並將其透過第15行寫入slave_register_0,再透過第16行將input enable的訊號寫入slave_register_1。
第20行則是去讀取PL端輸出的資料,從slave_register_2讀取。

  • PL端(sort.v):
    在LOAD狀態,將從PS端傳來的slv_reg0內的inData給一筆一筆存入至array中。
    等待電路經過i=1至n-1次COMPARE,SWAP以及LAST的循環後,array將會被排序完成,而outData會一直將array的每一個元素給串接起來,直到FSM跑至DONE,outData結果才是正確的,PS屆時讀取slv_reg2的值才會是正確的輸出。
// --------------------- // // array // // --------------------- // always@(posedge clk) begin if(rstn == 1'b0) begin array[0] <= 4'd0; ... end else begin if(curState == `LOAD) begin case(index) 5'd0: array[index] <= inData[3:0]; 5'd1: array[index] <= inData[7:4]; ... endcase end else if(curState == `SWAP) array[j+5'd1] <= array[j]; else if(curState == `LAST) array[j+5'd1] <= key; else begin array[0] <= array[0]; ... end end end // --------------------- // // outData // // --------------------- // assign outData = {array[7], array[6], array[5], array[4], array[3], array[2], array[1], array[0]};
運行流程


上方圖中輸入8筆資料先後由左到右依序為<3,15,1,4,14,6,0,9>,可以看到input顯示906e41f3,為將8筆資料存入同一個32-bit的u32暫存空間的結果,從LSB至MSB以16進制方式表示與輸入序列相吻合,代表輸入成功寫入slv_reg0。

Problem3-3

AXI interface

slv_reg0 -> type
slv_reg1 -> inValid
slv_reg2 -> inData
slv_reg3 -> dataResponse
slv_reg4 -> result
slv_reg5 -> outValid

State Diagram

state 說明
NOP 閒置狀態,不進行任何操作,並等待inValid訊號。
HANDLE 正在處理parity運算,並且在處理完成後拉高calcDone。
DONE 將outValid設為1,並等待PS發回dataResponse訊號 (確認已收到Output data) 後,回到NOP state等待下一次操作。
設計邏輯
  • PS端(main.c):
// pairty mode printf("Please choose pairty mode(1: odd, 2: even):\n\r"); scanf("%d", &type); while(type < 1 || type > 2){ ... } printf("%d\r\n", type); PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 0, type); // input data printf("Please input data (Support number to 2147483647(2^31) at most):\n\r"); scanf("%d", &inData); printf("%d\r\n", inData); printf("Also equal to 0x%x\r\n", inData); PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 4, inData);

上方程式碼用於確認使用者的input是否在允許範圍內,並且將value寫入對應的AXI slave register存取的位置。

// inValid high PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 8, 1); // Wait until the calculation is done while(PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 20) == 0){ ... if(PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 20) == 1){ // inValid low PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 8, 0); } } if(PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 20) == 1){ // inValid low PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 8, 0); } printf("Calculation is done!\r\n");

上方程式碼會將Processor hang在while迴圈直到確認ParityGenerator module已經做完運算(如果已經完成會發回outValid訊號)。

// ! Output switch (type) { case 1: printf("Odd pairty of 0x%x: %d\n", inData, PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 16)); break; case 2: printf("Even pairty of 0x%x: %d\n", inData, PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 16)); break; default: break; } // Told PL the data is received PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 12, 1); printf("====================================\n"); // Reset for(int i=0;i<7;i++){ printf("Reset slave register %d: %d\n",i,PARITYGENERATOR_mReadReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 4*i)); PARITYGENERATOR_mWriteReg(XPAR_PARITYGENERATOR_0_S00_AXI_BASEADDR, 4*i, 0); } printf("====================================\n");

最後會將從PL得到的Output結果print出來,告知PL已經收到data(將dataResponse對應的slave register拉高),並且將AXI slave register的data進行重置,進行下一次操作。

  • PL端(ParityGenerator.v):
// Registers reg calcDone; reg calc_reg, result_reg; // Wires assign result = result_reg; assign outValid = (state == `DONE) ? 1'b1 : 1'b0; integer counter; ... // --------------------- // // outData // // --------------------- // always @(posedge clk) begin case (state) `NOP: begin calc_reg <= 32'd0; result_reg <= 32'd0; calcDone <= 1'b0; inData_part_reg <= 0; end `HANDLE: begin if(counter >= 31) begin calc_reg <= calc_reg; calcDone <= 1'b1; end else begin calc_reg <= calc_reg ^ inData[counter]; inData_part_reg <= inData[counter]; end end `DONE: begin if(type == 1) begin result_reg <= ~calc_reg; end else begin result_reg <= calc_reg; end calcDone <= 1'b1; end default: begin calc_reg <= 32'd0; result_reg <= 32'd0; calcDone <= 1'b0; end endcase end // --------------------- // // counter // // --------------------- // always @(posedge clk) begin if(state == `HANDLE) begin counter <= counter + 1; end else counter <= 0; end

countercalc_reg為在HANDLE時主要作為運算的兩個register:

  • counter會在每個cycle遞增直到大於31。
  • calc_regNOP會重置為0,並且在HANDLE時和input data對應counter的bit進行XOR運算,最後在DONE根據不同的parity type將值傳給result_reg 進行output。

完整運行結果

  1. 透過ZYNQ Processor上運行的main.c選擇進行哪種運算
    (1: Arithmetic, 2: Sorting, 3: Parity Generator)
  2. Arithmetic:
    • Check if mode is valid
    • Add result
    • Sub result
    • Mul result
  3. Sorting:
    • Check if mode is valid
    • result
  4. Parity Generator:
    • Check if mode is valid
    • result