加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • SPI 協(xié)議驅動設計
  • 推薦器件
  • 相關推薦
  • 電子產業(yè)圖譜
申請入駐 產業(yè)圖譜

FPGA零基礎學習精選 | SPI 協(xié)議驅動設計

2023/07/17
2514
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

作者:郝旭帥??校對:陸輝

大俠好,歡迎來到FPGA技術江湖,江湖偌大,相見即是緣分。大俠可以關注FPGA技術江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

本系列將帶來FPGA的系統(tǒng)性學習,從最基本的數(shù)字電路基礎開始,最詳細操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、信息、通信類專業(yè)學生、初入職場小白及打算進階提升的職業(yè)開發(fā)者都可以有系統(tǒng)性學習的機會。

系統(tǒng)性的掌握技術開發(fā)以及相關要求,對個人就業(yè)以及職業(yè)發(fā)展都有著潛在的幫助,希望對大家有所幫助。后續(xù)會陸續(xù)更新 Xilinx 的 Vivado、ISE 及相關操作軟件的開發(fā)的相關內容,學習FPGA設計方法及設計思想的同時,實操結合各類操作軟件,會讓你在技術學習道路上無比的順暢,告別技術學習小BUG卡破腦殼,告別目前忽悠性的培訓誘導,真正的去學習去實戰(zhàn)應用。話不多說,上貨。

SPI 協(xié)議驅動設計

本篇實現(xiàn)基于叁芯智能科技的SANXIN -B01 FPGA開發(fā)板,以下為配套的教程,如有入手開發(fā)板,可以登錄官方淘寶店購買,還有配套的學習視頻。

SANXIN-B01 Verilog教程-郝旭帥團隊

SPI是串行外設接口(Serial Peripheral Interface)的縮寫。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節(jié)約了芯片的管腳,同時為PCB的布局上節(jié)省空間,提供方便,正是出于這種簡單易用的特性,如今越來越多的芯片集成了這種通信協(xié)議。

SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,中間靠三線或者四線連接(三線時為單向傳輸或者數(shù)據(jù)線雙向傳輸)。所有基于SPI的設備共有的,它們是MISO、MOSI、SCLK、CS。

MISO– Master Input Slave Output,主設備數(shù)據(jù)輸入,從設備數(shù)據(jù)輸出。

MOSI– Master Output Slave Input,主設備數(shù)據(jù)輸出,從設備數(shù)據(jù)輸入。

SCLK – Serial Clock,時鐘信號,由主設備產生。

CS – Chip Select,從設備使能信號,由主設備控制。

cs是從芯片是否被主芯片選中的控制信號,也就是說只有片選信號為預先規(guī)定的使能信號時(高電位或低電位),主芯片對此從芯片的操作才有效。這就使在同一條總線上連接多個spi設備成為可能。

通訊是通過數(shù)據(jù)交換完成的,由sclk提供時鐘脈沖,mosi、miso則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過 mosi線,數(shù)據(jù)在時鐘上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。因此,至少需要N次時鐘信號的改變(上沿和下沿為一次),才能完成N位數(shù)據(jù)的傳輸。

spi通信有四種不同的模式,不同的從設備可能在出廠時就已經(jīng)配置為某種模式。通信的雙方必須是工作在同一模式下,所以我們可以對主設備的spi模式進行配置,通過CPOL(時鐘極性)和CPHA(時鐘相位)來控制我們主設備的通信模式。

mode0:CPOL=0,CPHA=0;

mode1:CPOL=0,CPHA=1;

mode2:CPOL=1,CPHA=0;

mode3:CPOL=1,CPHA=1;

時鐘極性CPOL是用來配置SCLK在空閑時,應該處于的狀態(tài);時鐘相位CPHA用來配置在第幾個邊沿進行采樣。

CPOL=0,表示在空閑狀態(tài)時,時鐘SCLK為低電平

CPOL=1,表示在空閑狀態(tài)時,時鐘SCLK為高電平

CPHA=0,表示數(shù)據(jù)采樣是在第1個邊沿。

CPHA=1,表示數(shù)據(jù)采樣是在第2個邊沿。

即:

CPOL=0,CPHA=0:此時空閑態(tài)時,SCLK處于低電平,數(shù)據(jù)采樣是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。

CPOL=0,CPHA=1:此時空閑態(tài)時,SCLK處于低電平,數(shù)據(jù)發(fā)送是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。

CPOL=1,CPHA=0:此時空閑態(tài)時,SCLK處于高電平,數(shù)據(jù)采集是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。

CPOL=1,CPHA=1:此時空閑態(tài)時,SCLK處于高電平,數(shù)據(jù)發(fā)送是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。

硬件簡介

FLASH閃存 的英文名稱是"Flash Memory",一般簡稱為"Flash",它屬于內存器件的一種,是一種非易失性( Non-Volatile )內存。

在開發(fā)板上有一塊flash(M25P16),用來保存FPGA的硬件配置信息,也可以用來存儲用戶的應用程序或數(shù)據(jù)。

M25P16是一款帶有寫保護機制和高速SPI總線訪問的2M字節(jié)串行Flash存儲器,該存儲器主要特點:2M字節(jié)的存儲空間,分32個扇區(qū),每個扇區(qū)256頁,每頁256字節(jié);能單個扇區(qū)擦除和整片擦除;每扇區(qū)擦寫次數(shù)保證10萬次、數(shù)據(jù)保存期限至少20年。

C(serial clock:串行時鐘)為D和Q提供了數(shù)據(jù)輸入或者輸出的時序。D的數(shù)據(jù)總是在C的上升沿被采樣。Q的數(shù)據(jù) 在C的下降沿被輸出。

(Chip Select:芯片選擇端),當輸入為低時,該芯片被選中,可以允許進行讀寫操作。當輸入為高時,該芯片被釋放,不能夠進行操作。

對于H——o——l——d——和W——, 為保持功能和硬件寫保護功能,在本設計中不使用此管腳,在硬件設計時,這兩個管腳全部被拉高了,即全部失效。

flash采用spi的通信協(xié)議,flash當做從機。serial clcok等效于spi中的sclk,chip select等效于spi中的cs,D等效于spi中的mosi,Q等效于spi中的miso。

flash可以支持mode0和mode3,這兩種模式中,都是在時鐘的上升沿采樣,在時鐘的下降沿發(fā)送數(shù)據(jù)。

flash的每一頁都可以被寫入,但是寫入只能是把1改變?yōu)?。擦除可以把0改變?yōu)?。所以在正常寫入數(shù)據(jù)之前,都要將flash進行擦除。

flash的命令表如下:

下面介紹幾個常用的命令。

RDID(Read Identification :讀ID):發(fā)送命令RDID(9F),然后接收第1個字節(jié)的memory type(20H),第二個字節(jié)的memory capacity(15H)。后續(xù)的字節(jié)暫不關心。

WREN(Write Enable :寫使能):在任何寫或者擦除的命令之前,都必須首先打開寫使能。打開寫使能為發(fā)送命令WREN(06h)。

RDSR(Read Status Register:讀狀態(tài)寄存器):發(fā)送命令RDSR(05h),然后返回一個字節(jié)的狀態(tài)值。

狀態(tài)寄存器的格式如下:

WIP(Write In Progress bit)表示flash內部是否正在進行內部操作,寫和擦除都會導致flash內部進行一段時間的工作,在內部工作期間,外部的命令會被忽略,所以在進行任何命令之前,都需要查看flash內部是否正在工作。WIP為1時,表示flash內部正在工作;WIP為0時,表示flash內部沒有在工作。

READ(Read DATA Bytes:讀數(shù)據(jù)):發(fā)送命令READ(03H),后續(xù)發(fā)送3個字節(jié)的地址,然后就可以接收數(shù)據(jù),內部的地址會不斷遞增。一個讀命令就可以把整個flash全部讀完。

PP(Page Program :頁編寫):發(fā)送命令PP(02H),接著發(fā)送3個字節(jié)的地址,然后發(fā)送數(shù)據(jù)即可。切記所寫的數(shù)據(jù)不能超過本頁的地址范圍。

SE(Sector Erase :扇區(qū)擦除):發(fā)送命令SE(D8H),接著發(fā)送3個字節(jié)的地址。

BE(Bulk Erase:整片擦除):發(fā)送命令BE(C7H)。

關于flash的其他的介紹,可以參考03_芯片手冊->FLASH->M25P16.pdf。

設計要求

設計flash(M25P16)控制器。

設計分析

根據(jù)M25P16的數(shù)據(jù)手冊得知,其接口為spi接口,且支持模式0和模式3,本設計中選擇模式0。

輸入時序圖如下:

輸出時序如下:

時序圖中所對應的符號說明:

根據(jù)輸入和輸出的時序圖以及參數(shù)表,將SPI的時鐘的頻率定為10MHz。

在設計中,F(xiàn)PGA作為主機,M25P16作為從機。

架構設計和信號說明

此模塊命名為m25p16_drive。

二級模塊(分模塊)(第一頁)

二級模塊(分模塊)(第二頁)

設計中,各個命令單獨寫出控制器,通過多路選擇器選擇出對應的命令,然后控制spi_8bit_drive將數(shù)據(jù)按照spi的協(xié)議發(fā)送出去。各個命令的脈沖通過ctrl模塊進行控制各個命令控制器,寫入的數(shù)據(jù)首先寫入到寫緩沖區(qū),讀出的數(shù)據(jù)讀出后寫入到讀緩沖區(qū)。

暫不分配的端口,在應用時都是由上游模塊進行控制,本設計測試時,編寫上游模塊進行測試。

各個模塊的功能,和連接線的功能在各個模塊設計中說明。

spi_8bit_drive設計實現(xiàn)

本模塊負責將8bit的并行數(shù)據(jù)按照spi協(xié)議發(fā)送出去,以及負責按照spi協(xié)議接收數(shù)據(jù),將接收的數(shù)據(jù)(8bit)并行傳輸給各個模塊。

spi_send_en為發(fā)送數(shù)據(jù)使能信號(脈沖信號),spi_send_data為所要發(fā)送數(shù)據(jù),spi_send_done為發(fā)送完成信號(脈沖信號)。

spi_read_en為接收數(shù)據(jù)使能信號(脈沖信號),spi_read_data為所接收的數(shù)據(jù),spi_read_done為接收完成信號(脈沖信號)。

module spi_8bit_drive (
  input   wire              clk,  input   wire              rst_n,    input   wire              spi_send_en,  input   wire  [7:0]       spi_send_data,  output  reg               spi_send_done,    input   wire              spi_read_en,  output  reg   [7:0]       spi_read_data,  output  reg               spi_read_done,    output  wire              spi_sclk,  output  wire              spi_mosi,  input   wire              spi_miso);
  reg           [8:0]       send_data_buf;  reg           [3:0]       send_cnt;  reg                       rec_en;  reg                       rec_en_n;  reg           [3:0]       rec_cnt;  reg           [7:0]       rec_data_buf;    always @ (negedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      send_data_buf <= 9'd0;    else      if (spi_send_en == 1'b1)        send_data_buf <= {spi_send_data, 1'b0};      else        send_data_buf <= send_data_buf;  end    always @ (negedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      send_cnt <= 4'd8;    else      if (spi_send_en == 1'b1)        send_cnt <= 4'd0;      else        if (send_cnt < 4'd8)          send_cnt <= send_cnt + 1'b1;        else          send_cnt <= send_cnt;  end    assign spi_mosi = send_data_buf[8 - send_cnt];  assign spi_sclk = (send_cnt < 4'd8 || rec_en_n == 1'b1) ? clk : 1'b0;    always @ (negedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      spi_send_done <= 1'b0;    else      if (send_cnt == 4'd7)        spi_send_done <= 1'b1;      else        spi_send_done <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rec_en <= 1'b0;    else      if (spi_read_en == 1'b1)        rec_en <= 1'b1;      else        if (rec_cnt ==  4'd7)          rec_en <= 1'b0;        else          rec_en <= rec_en;  end    always @ (negedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rec_en_n <= 1'b0;    else      rec_en_n <= rec_en;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rec_data_buf <= 8'd0;    else      if (rec_en == 1'b1)        rec_data_buf <= {rec_data_buf[6:0], spi_miso};      else        rec_data_buf <=rec_data_buf;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rec_cnt <= 4'd0;    else      if (rec_en == 1'b1)        rec_cnt <= rec_cnt + 1'b1;      else        rec_cnt <= 4'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      spi_read_done <= 1'b0;    else      if (rec_cnt == 4'd8)        spi_read_done <= 1'b1;      else        spi_read_done <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      spi_read_data <= 8'd0;    else      if (rec_cnt == 4'd8)        spi_read_data <= rec_data_buf;      else        spi_read_data <= spi_read_data;  end  endmodule

在發(fā)送邏輯控制中,全部的信號采用下降沿驅動。利用外部給予的spi_send_en作為啟動信號,啟動send_cnt。send_cnt在不發(fā)送數(shù)據(jù)時為8,發(fā)送數(shù)據(jù)時,從0到7。

在接收邏輯中,全部的信號采用上升沿驅動。利用外部給予的spi_read_en作為啟動信號,啟動rec_en,經(jīng)過移位接收數(shù)據(jù)。

在spi_sclk輸出時,采用組合邏輯。由于設計采用spi的模式0,故而spi_sclk不發(fā)送或者接收數(shù)據(jù)時為0,接收數(shù)據(jù)時為時鐘信號。因為要求為模式0,所以在接收數(shù)據(jù)時,spi_sclk的輸出不能夠先有下降沿,即要求spi_sclk的控制信號不能由上升沿信號驅動,所以將rec_en同步到下降沿的rec_en_n。

仿真代碼為:

`timescale 1ns/1ps
module spi_8bit_drive_tb;
  reg               clk;  reg               rst_n;    reg               spi_send_en;  reg     [7:0]     spi_send_data;  wire              spi_send_done;    reg               spi_read_en;  wire    [7:0]     spi_read_data;  wire              spi_read_done;    wire              spi_sclk;  wire              spi_mosi;  reg               spi_miso;
  spi_8bit_drive spi_8bit_drive_inst(
      .clk              (clk),      .rst_n            (rst_n),            .spi_send_en      (spi_send_en),      .spi_send_data    (spi_send_data),      .spi_send_done    (spi_send_done),            .spi_read_en      (spi_read_en),      .spi_read_data    (spi_read_data),      .spi_read_done    (spi_read_done),            .spi_sclk         (spi_sclk),      .spi_mosi         (spi_mosi),      .spi_miso         (spi_miso)  );    initial clk = 1'b0;  always # 50 clk = ~clk;    initial begin    rst_n = 1'b0;    spi_send_en = 1'b0;    spi_send_data = 8'd0;    spi_read_en = 1'b0;    spi_miso = 1'b0;    # 201    rst_n = 1'b1;    # 200    @ (posedge clk);    # 2;    spi_send_en = 1'b1;    spi_send_data = {$random} % 256;    @ (posedge clk);    # 2;    spi_send_en = 1'b0;    spi_send_data = 8'd0;        @ (posedge spi_send_done);    # 2000    @ (posedge clk);    # 2;    spi_read_en = 1'b1;    @ (posedge clk);    # 2;    spi_read_en = 1'b0;        @ (posedge spi_read_done);    # 200    $stop;  end    always @ (negedge clk) spi_miso <= {$random} % 2;
endmodule

在仿真中,將時鐘設置為10MHz。

所有的信號采用上升沿驅動。發(fā)送一個8bit的隨機數(shù)值,接收一個8bit的隨機數(shù)值。

spi_miso信號為從機下降沿驅動信號。

通過RTL仿真,可以看出發(fā)送和接收全部正常。

mux7_1設計實現(xiàn)

本模塊負責將7個命令模塊發(fā)出的命令(寫使能、寫數(shù)據(jù)和讀使能)經(jīng)過選擇發(fā)送給spi_8bit_drive模塊。

module mux7_1 (
  input     wire          rdsr_send_en,  input     wire    [7:0] rdsr_send_data,  input     wire          rdsr_read_en,    input     wire          pp_send_en,  input     wire    [7:0] pp_send_data,    input     wire          wren_send_en,  input     wire    [7:0] wren_send_data,    input     wire          be_send_en,  input     wire    [7:0] be_send_data,    input     wire          se_send_en,  input     wire    [7:0] se_send_data,    input     wire          rdid_send_en,  input     wire    [7:0] rdid_send_data,  input     wire          rdid_read_en,    input     wire          read_send_en,  input     wire    [7:0] read_send_data,  input     wire          read_read_en,    input     wire    [2:0] mux_sel,                     output    reg           spi_send_en,  output    reg     [7:0] spi_send_data,  output    reg           spi_read_en);
  always @ * begin    case (mux_sel)      3'd0    : begin         spi_send_en = rdsr_send_en;         spi_send_data = rdsr_send_data;         spi_read_en = rdsr_read_en;       end      3'd1    : begin         spi_send_en = pp_send_en;         spi_send_data = pp_send_data;         spi_read_en = 1'b0;       end      3'd2    : begin         spi_send_en = wren_send_en;         spi_send_data = wren_send_data;         spi_read_en = 1'b0;       end      3'd3    : begin         spi_send_en = be_send_en;         spi_send_data = be_send_data;         spi_read_en = 1'b0;       end      3'd4    : begin         spi_send_en = se_send_en;         spi_send_data = se_send_data;         spi_read_en = 1'b0;       end      3'd5    : begin         spi_send_en = rdid_send_en;         spi_send_data = rdid_send_data;         spi_read_en = rdid_read_en;       end      3'd6    : begin         spi_send_en = read_send_en;         spi_send_data = read_send_data;         spi_read_en = read_read_en;       end      default : begin         spi_send_en = 1'b0;         spi_send_data = 8'd0;         spi_read_en = 1'b0;       end    endcase  end
endmodule

在設計中,有的命令模塊不需要進行讀?。╬p和se等等),此時將輸出的讀使能信號輸出為低電平。

be設計實現(xiàn)

該模塊接收到be_en(整片擦除的脈沖信號)信號后,發(fā)送對應的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,輸出擦除完成的脈沖。

module be (
  input     wire            clk,  input     wire            rst_n,    input     wire            be_en,  output    reg             be_done,    output    reg             be_send_en,  output    wire    [7:0]   be_send_data,  input     wire            spi_send_done);
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      be_send_en <= 1'b0;    else      be_send_en <= be_en;  end    assign be_send_data = 8'hc7;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      be_done <= 1'b0;    else      be_done <= spi_send_done;  end  endmodule

整片擦除的命令為8’hc7。

wren設計實現(xiàn)

該模塊接收到wren_en(打開flash內部的寫使能的脈沖信號)信號后,發(fā)送對應的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,輸出擦除完成的脈沖。

module wren (
  input     wire            clk,  input     wire            rst_n,    input     wire            wren_en,  output    reg             wren_done,    output    reg             wren_send_en,  output    wire    [7:0]   wren_send_data,  input     wire            spi_send_done);
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren_send_en <= 1'b0;    else      wren_send_en <= wren_en;  end    assign wren_send_data = 8'h06;
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren_done <= 1'b0;    else      wren_done <= spi_send_done;  end  endmodule

打開flash內部寫使能的命令碼為8’h06。

se設計實現(xiàn)

該模塊接收到se_en(擦除扇區(qū)的寫使能的脈沖信號)信號后,發(fā)送對應的使能和數(shù)據(jù),等待發(fā)送完成脈沖。發(fā)送完成后,接著發(fā)送高八位地址,中間八位地址和低八位地址。全部發(fā)送完成后,發(fā)送se_done信號。

該模塊采用狀態(tài)機實現(xiàn)。SE_STATE(扇區(qū)擦除命令發(fā)送)、H_ADDR(高八位地址發(fā)送)、M_ADDR(中間八位地址發(fā)送)、L_ADDR(低八位地址發(fā)送)、SE_DONE(扇區(qū)擦除完成)。所有的脈沖信號在未標注的時刻,輸出全部為0。

設計代碼為:

module se (
  input   wire                clk,  input   wire                rst_n,    input   wire                se_en,  input   wire      [23:0]    se_addr,  output  reg                 se_done,    output  reg                 se_send_en,  output  reg       [7:0]     se_send_data,  input   wire                spi_send_done);
  localparam      SE_STATE    = 5'b00001;  localparam      H_ADDR      = 5'b00010;  localparam      M_ADDR      = 5'b00100;  localparam      L_ADDR      = 5'b01000;  localparam      SE_DONE     = 5'b10000;    reg               [4:0]     c_state;  reg               [4:0]     n_state;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= SE_STATE;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      SE_STATE    :   begin        if (se_en == 1'b0)          n_state = SE_STATE;        else          n_state = H_ADDR;      end            H_ADDR      :   begin        if (spi_send_done == 1'b0)          n_state = H_ADDR;        else          n_state = M_ADDR;      end            M_ADDR      :   begin        if (spi_send_done == 1'b0)          n_state = M_ADDR;        else          n_state = L_ADDR;      end            L_ADDR      :   begin        if (spi_send_done == 1'b0)          n_state = L_ADDR;        else          n_state = SE_DONE;      end            SE_DONE     :   begin        if (spi_send_done == 1'b0)          n_state = SE_DONE;        else          n_state = SE_STATE;      end            default     :     n_state = SE_STATE;    endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      se_send_en <= 1'b0;    else      case (c_state)        SE_STATE    :   begin          if (se_en == 1'b1)            se_send_en <= 1'b1;          else            se_send_en <= 1'b0;        end                H_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_en <= 1'b1;          else            se_send_en <= 1'b0;        end                M_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_en <= 1'b1;          else            se_send_en <= 1'b0;        end                L_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_en <= 1'b1;          else            se_send_en <= 1'b0;        end                SE_DONE     :   begin          se_send_en <= 1'b0;        end                default     :   se_send_en <= 1'b0;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      se_send_data <= 8'd0;    else      case (c_state)        SE_STATE    :   begin          if (se_en == 1'b1)            se_send_data <= 8'hd8;          else            se_send_data <= 8'd0;        end              H_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_data <= se_addr[23:16];          else            se_send_data <= 8'd0;        end                M_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_data <= se_addr[15:8];          else            se_send_data <= 8'd0;        end                L_ADDR      :   begin          if (spi_send_done == 1'b1)            se_send_data <= se_addr[7:0];          else            se_send_data <= 8'd0;        end                SE_DONE     :   begin          se_send_data <= 8'd0;        end                default     :   se_send_data <= 8'd0;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      se_done <= 1'b0;    else      if (c_state == SE_DONE && spi_send_done == 1'b1)        se_done <= 1'b1;      else        se_done <= 1'b0;  end  endmodule

在發(fā)送過程中,由于是每8bit發(fā)送一次,所以在時序上將看到發(fā)送時,每8個脈沖一組,中間會有明顯的間隔。

pp設計實現(xiàn)

該模塊負責將外部寫fifo中的數(shù)據(jù)寫入到flash中。wr_fifo_rd為寫fifo的讀使能信號,wrdata為從寫fifo中讀出的數(shù)據(jù),wr_len為需要寫入flash中數(shù)據(jù)的長度,wr_addr為寫入地址。

該模塊采用狀態(tài)機實現(xiàn)。PP_STATE(發(fā)送pp命令),H_ADDR(發(fā)送高八位地址)、M_ADDR(發(fā)送中間八位地址),L_ADDR(發(fā)送低八位地址)、RDFIFO(讀寫fifo)、FIFO_WAIT(等待讀寫fifo的數(shù)據(jù)輸出)、SEND(發(fā)送8bit數(shù)據(jù))、SEND_WAIT(發(fā)送等待,發(fā)送完成后判斷是否發(fā)送完成)。對于所有的脈沖信號,沒有賦值的位置,全部賦值為0。

cnt為記錄已經(jīng)發(fā)送的數(shù)據(jù)個數(shù)。

設計代碼為:

module pp (
  input   wire                  clk,  input   wire                  rst_n,    input   wire                  pp_en,  output  reg                   pp_done,  output  reg                   wr_fifo_rd,  input   wire    [7:0]         wrdata,  input   wire    [8:0]         wr_len,  input   wire    [23:0]        wr_addr,    output  reg                   pp_send_en,  output  reg     [7:0]         pp_send_data,  input   wire                  spi_send_done);
  localparam      PP_STATE    = 8'b0000_0001;  localparam      H_ADDR      = 8'b0000_0010;  localparam      M_ADDR      = 8'b0000_0100;  localparam      L_ADDR      = 8'b0000_1000;  localparam      RDFIFO      = 8'b0001_0000;  localparam      FIFO_WAIT   = 8'b0010_0000;  localparam      SEND        = 8'b0100_0000;  localparam      SEND_WAIT   = 8'b1000_0000;    reg             [7:0]         c_state;  reg             [7:0]         n_state;  reg             [8:0]         cnt;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= PP_STATE;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      PP_STATE        :   begin        if (pp_en == 1'b0)          n_state = PP_STATE;        else          n_state = H_ADDR;      end            H_ADDR          :   begin        if (spi_send_done == 1'b1)          n_state = M_ADDR;        else          n_state = H_ADDR;      end            M_ADDR          :   begin        if (spi_send_done == 1'b1)          n_state = L_ADDR;        else          n_state = M_ADDR;      end            L_ADDR          :   begin        if (spi_send_done == 1'b1)          n_state = RDFIFO;        else          n_state = L_ADDR;      end            RDFIFO          :   begin        if (spi_send_done == 1'b1)          n_state = FIFO_WAIT;        else          n_state = RDFIFO;      end            FIFO_WAIT       :   begin        n_state = SEND;      end            SEND            :   begin        n_state = SEND_WAIT;      end            SEND_WAIT       :   begin        if (spi_send_done == 1'b1)          if (cnt == wr_len)            n_state = PP_STATE;          else            n_state = FIFO_WAIT;        else          n_state = SEND_WAIT;      end            default       :   n_state = PP_STATE;          endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      pp_send_en <= 1'b0;    else      case (c_state)        PP_STATE      :   begin          if  (pp_en == 1'b1)            pp_send_en <= 1'b1;          else            pp_send_en <= 1'b0;        end              H_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_en <= 1'b1;          else            pp_send_en <= 1'b0;        end                M_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_en <= 1'b1;          else            pp_send_en <= 1'b0;        end                L_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_en <= 1'b1;          else            pp_send_en <= 1'b0;        end                SEND          : begin          pp_send_en <= 1'b1;        end                default       :   pp_send_en <= 1'b0;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      pp_send_data <= 8'd0;    else      case (c_state)        PP_STATE      :   begin          if  (pp_en == 1'b1)            pp_send_data <= 8'h02;          else            pp_send_data <= 8'd0;        end              H_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_data <= wr_addr[23:16];          else            pp_send_data <= 8'd0;        end                M_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_data <= wr_addr[15:8];          else            pp_send_data <= 8'd0;        end                L_ADDR        :   begin          if (spi_send_done == 1'b1)            pp_send_data <= wr_addr[7:0];          else            pp_send_data <= 8'd0;        end                SEND          : begin          pp_send_data <= wrdata;        end                default       :   pp_send_data <= 8'd0;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      pp_done <= 1'b0;    else      if (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt == wr_len)        pp_done <= 1'b1;      else        pp_done <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 9'd0;    else      if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))        cnt <= cnt + 1'b1;      else        if (c_state == PP_STATE)          cnt <= 9'd0;        else          cnt <= cnt;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_fifo_rd <= 1'b1;    else      if ((c_state == RDFIFO && spi_send_done == 1'b1) || (c_state == SEND_WAIT && spi_send_done == 1'b1 && cnt < wr_len))        wr_fifo_rd <= 1'b1;      else        wr_fifo_rd <= 1'b0;  end  endmodule

rdsr設計實現(xiàn)

本模塊的功能為讀取m25p16的狀態(tài)寄存器,主要檢測狀態(tài)寄存器的最低位(WIP)。

WIP(write in progress :正在進行寫進程),該bie位表示了flash內部是否在進行寫進程。如果處于寫進程時,flash忽略外部所有的命令,所以建議在執(zhí)行任何命令前,首先進行檢測該位。1表示正在寫進程中,0表示不處于寫進程。

如果檢測到正在寫進程中,進行延遲1ms,然后再次讀取該位狀態(tài)。直到寫進程結束。

本模塊采用狀態(tài)機設計實現(xiàn)。ILDE(發(fā)送讀狀態(tài)寄存器命令)、RDSRSTATE(發(fā)送讀使能)、WIP(判斷wip位)、 DELAY1ms(延遲1ms)。cnt為延遲1ms的計數(shù)器。

設計代碼為:

module rdsr (
  input   wire                    clk,  input   wire                    rst_n,    input   wire                    rdsr_en,  output  reg                     rdsr_done,    output  reg                     rdsr_send_en,  output  reg       [7:0]         rdsr_send_data,  input   wire                    spi_send_done,    output  reg                     rdsr_read_en,  input   wire      [7:0]         spi_read_data,  input   wire                    spi_read_done);
  parameter     T_1ms           =       50_000;      localparam    IDLE            =       4'b0001;  localparam    RDSRSTATE       =       4'b0010;  localparam    WIP             =       4'b0100;  localparam    DELAY1ms        =       4'b1000;    reg               [3:0]         c_state;  reg               [3:0]         n_state;  reg               [15:0]        cnt;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end
  always @ * begin    case (c_state)      IDLE          :   begin        if (rdsr_en == 1'b0)          n_state = IDLE;        else          n_state = RDSRSTATE;      end            RDSRSTATE     :   begin        if (spi_send_done == 1'b1)          n_state = WIP;        else          n_state = RDSRSTATE;      end            WIP           :   begin        if (spi_read_done == 1'b0)          n_state = WIP;        else          if (spi_read_data[0] == 1'b0)            n_state = IDLE;          else            n_state = DELAY1ms;      end            DELAY1ms      :   begin        if (cnt < T_1ms - 1'b1)          n_state = DELAY1ms;        else          n_state = RDSRSTATE;      end            default       :   n_state = IDLE;        endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 16'd0;    else      if (c_state == DELAY1ms && cnt < T_1ms - 1'b1)        cnt <= cnt + 1'b1;      else        cnt <= 16'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdsr_done <= 1'b0;    else      if (c_state == WIP && spi_read_done == 1'b1 && spi_read_data[0] == 1'b0)        rdsr_done <= 1'b1;      else        rdsr_done <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdsr_send_data <= 8'd0;    else      if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))        rdsr_send_data <= 8'h05;      else        rdsr_send_data <= 8'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdsr_send_en <= 1'b0;    else      if ((c_state == IDLE && rdsr_en == 1'b1) || (c_state == DELAY1ms && cnt == T_1ms - 1'b1))        rdsr_send_en <= 1'b1;      else        rdsr_send_en <= 1'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdsr_read_en <= 1'b0;    else      if (c_state == RDSRSTATE && spi_send_done == 1'b1)        rdsr_read_en <= 1'b1;      else        rdsr_read_en <= 1'b0;  end  endmodule

rdid設計實現(xiàn)

該模塊負責讀取flash的ID(2015),驗證ID的正確性。

該模塊采用狀態(tài)機的方式實現(xiàn)。IDLE(等待讀取ID的命令)、IDSTATE1(讀取高八位ID)、IDSTATE2(讀取中間八位ID)、IDSTATE3(讀取低八位ID)、ID_CHECK(檢測ID的正確性)。

狀態(tài)轉移圖如下:

設計代碼為:

module rdid (
  input     wire                  clk,  input     wire                  rst_n,    input     wire                  rdid_en,  output    reg                   rdid_done,    output    reg                   rdid_send_en,  output    reg     [7:0]         rdid_send_data,  input     wire                  spi_send_done,    output    reg                   rdid_read_en,  input     wire                  spi_read_done,  input     wire    [7:0]         spi_read_data);
  localparam        IDLE          = 5'b00001;  localparam        IDSTATE1      = 5'b00010;  localparam        IDSTATE2      = 5'b00100;  localparam        IDSTATE3      = 5'b01000;  localparam        ID_CHECK      = 5'b10000;    reg               [4:0]         c_state;  reg               [4:0]         n_state;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= IDLE;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      IDLE          :     begin        if (rdid_en == 1'b1)          n_state = IDSTATE1;        else          n_state = IDLE;      end            IDSTATE1      :     begin        if (spi_send_done == 1'b1)          n_state = IDSTATE2;        else          n_state = IDSTATE1;      end            IDSTATE2      :     begin        if (spi_read_done == 1'b1 && spi_read_data == 8'h20)          n_state = IDSTATE3;        else          n_state = IDSTATE2;      end            IDSTATE3      :     begin        if (spi_read_done == 1'b1 && spi_read_data == 8'h20)          n_state = ID_CHECK;        else          n_state = IDSTATE3;      end            ID_CHECK      :     begin        if (spi_read_done == 1'b1 && spi_read_data == 8'h15)          n_state = IDLE;        else          n_state = ID_CHECK;      end            default       :   n_state = IDLE;    endcase  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdid_send_data <= 8'd0;    else      if (c_state == IDLE && rdid_en == 1'b1)        rdid_send_data <= 8'h9f;      else        rdid_send_data <= 8'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdid_send_en <= 1'b0;    else      if (c_state == IDLE && rdid_en == 1'b1)        rdid_send_en <= 1'b1;      else        rdid_send_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdid_read_en <= 1'b0;    else      if ((c_state == IDSTATE1 && spi_send_done == 1'b1) || (c_state == IDSTATE2 && spi_read_done == 1'b1 && spi_read_data == 8'h20) || (c_state == IDSTATE3 && spi_read_done == 1'b1 && spi_read_data == 8'h20))        rdid_read_en <= 1'b1;      else        rdid_read_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdid_done <= 1'b0;    else      if (c_state == ID_CHECK && spi_read_done == 1'b1 && spi_read_data == 8'h15)        rdid_done <= 1'b1;      else        rdid_done <= 1'b0;  end  endmodule

read_ctrl設計實現(xiàn)

該模塊負責將flash的數(shù)據(jù)讀出,寫入到輸出緩存中。

該模塊采用狀態(tài)機實現(xiàn)。RD_STATE(等待讀命令)、H_ADDR(發(fā)送高八位地址)、M_ADDR(發(fā)送中間八位地址)、L_ADDR(發(fā)送低八位地址)、RDDATA(讀取數(shù)據(jù))、WRFIFO(將讀出的數(shù)據(jù)寫入到FIFO中)、CHECK_LEN(判斷讀取的長度)。

狀態(tài)轉移圖如下:

設計代碼為:

module read_ctrl (
  input     wire                clk,  input     wire                rst_n,    input     wire                read_en,  input     wire    [23:0]      rd_addr,  input     wire    [8:0]       rd_len,    output    reg     [7:0]       rddata,  output    reg                 rd_fifo_wr,    output    reg                 read_done,    output    reg                 read_send_en,  output    reg     [7:0]       read_send_data,  input     wire                spi_send_done,    output    reg                 read_read_en,  input     wire                spi_read_done,  input     wire    [7:0]       spi_read_data);
  localparam        RD_STATE    = 7'b000_0001;  localparam        H_ADDR      = 7'b000_0010;  localparam        M_ADDR      = 7'b000_0100;  localparam        L_ADDR      = 7'b000_1000;  localparam        RDDATA      = 7'b001_0000;  localparam        WRFIFO      = 7'b010_0000;  localparam        CHECK_LEN   = 7'b100_0000;    reg               [6:0]       c_state;  reg               [6:0]       n_state;  reg               [8:0]       cnt;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= RD_STATE;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      RD_STATE      :   begin        if (read_en == 1'b1)          n_state = H_ADDR;        else          n_state = RD_STATE;      end            H_ADDR        :   begin        if (spi_send_done == 1'b1)          n_state = M_ADDR;        else          n_state = H_ADDR;      end            M_ADDR        :   begin        if (spi_send_done == 1'b1)          n_state = L_ADDR;        else          n_state = M_ADDR;      end            L_ADDR        :   begin        if (spi_send_done == 1'b1)          n_state = RDDATA;        else          n_state = L_ADDR;      end            RDDATA        :   begin        if (spi_send_done == 1'b1)          n_state = WRFIFO;        else          n_state = RDDATA;      end            WRFIFO        :   begin        if (spi_read_done == 1'b1)          n_state = CHECK_LEN;        else          n_state = WRFIFO;      end            CHECK_LEN     :   begin        if (cnt == rd_len)          n_state = RD_STATE;        else          n_state = WRFIFO;      end            default       :   n_state = RD_STATE;        endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      cnt <= 9'd0;    else      if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))        cnt <= cnt + 1'b1;      else        if (c_state == RD_STATE)          cnt <= 9'd0;        else            cnt <= cnt;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_read_en <= 1'b0;    else      if ((c_state == RDDATA && spi_send_done == 1'b1) || (c_state == CHECK_LEN && cnt < rd_len))        read_read_en <= 1'b1;      else        read_read_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_done <= 1'b0;    else      if (c_state == CHECK_LEN && cnt == rd_len)        read_done <= 1'b1;      else        read_done <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_fifo_wr <= 1'b0;    else      if (c_state == WRFIFO && spi_read_done == 1'b1)        rd_fifo_wr <= 1'b1;      else        rd_fifo_wr <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rddata <= 8'd0;    else      if (c_state == WRFIFO && spi_read_done == 1'b1)        rddata <= spi_read_data;      else        rddata <= 8'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_send_en <= 1'b0;    else      case (c_state)        RD_STATE      :   begin          if  (read_en == 1'b1)            read_send_en <= 1'b1;          else            read_send_en <= 1'b0;        end              H_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_en <= 1'b1;          else            read_send_en <= 1'b0;        end                M_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_en <= 1'b1;          else            read_send_en <= 1'b0;        end                L_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_en <= 1'b1;          else            read_send_en <= 1'b0;        end             default       :   read_send_en <= 1'b0;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_send_data <= 8'd0;    else      case (c_state)        RD_STATE      :   begin          if  (read_en == 1'b1)            read_send_data <= 8'h03;          else            read_send_data <= 8'd0;        end              H_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_data <= rd_addr[23:16];          else            read_send_data <= 8'd0;        end                M_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_data <= rd_addr[15:8];          else            read_send_data <= 8'd0;        end                L_ADDR        :   begin          if (spi_send_done == 1'b1)            read_send_data <= rd_addr[7:0];          else            read_send_data <= 8'd0;        end                default       :   read_send_data <= 8'd0;      endcase  end  endmodule

wr_fifo和rd_fifo調用

兩個fifo的寬度設置為8,深度設置為256,同步fifo,帶有復位。

ctrl設計實現(xiàn)

該模塊根據(jù)外部的命令,按照m25p16的執(zhí)行規(guī)則,進行控制各個模塊的執(zhí)行。

該模塊采用狀態(tài)機實現(xiàn)。INIT_RDSR(讀WIP),INIT_RDID(讀ID),INIT_ID(判斷ID),WIP(讀WIP),WIP_DONE(等待WIP),IDLE(空閑狀態(tài)),**STATE(執(zhí)行對應的命令),**WREN(打開flash的寫使能)。在進行任何命令前,都檢查wip。

狀態(tài)轉移圖如下:

在不同的狀態(tài),mux_sel選擇對應的命令通過。

drive_busy只有在IDLE狀態(tài)才是低電平。

spi_cs_n信號, DLE狀態(tài)為高電平、WIP_DONE(INIT_RDID)中spi_read_done信號為高時 (保證能夠多次讀取狀態(tài)寄存器)、在其他狀態(tài)發(fā)生切換時,spi_cs_n 為高電平,否則為低電平。

設計代碼為:

module ctrl (
  input     wire                clk,  input     wire                rst_n,    input     wire                flag_be,  input     wire                flag_se,  input     wire                flag_wr,  input     wire                flag_rd,  input     wire    [23:0]      addr,  input     wire    [8:0]       len,    output    wire                drive_busy,    output    reg                 spi_cs_n,    input     wire                spi_read_done,  output    reg                 rdsr_en,  input     wire                rdsr_done,    output    reg                 wren_en,  input     wire                wren_done,    output    reg                 pp_en,  output    reg     [23:0]      wr_addr,  output    reg     [8:0]       wr_len,  input     wire                pp_done,    output    reg                 be_en,  input     wire                be_done,    output    reg                 se_en,  output    reg     [23:0]      se_addr,  input     wire                se_done,    output    reg                 rdid_en,  input     wire                rdid_done,    output    reg                 read_en,  output    reg     [23:0]      rd_addr,  output    reg     [8:0]       rd_len,  input     wire                read_done,    output    reg     [2:0]       mux_sel);
  localparam        INIT_RDSR       =   13'b0000_0000_00001;  localparam        INIT_RDID       =   13'b0000_0000_00010;  localparam        INIT_ID         =   13'b0000_0000_00100;  localparam        WIP             =   13'b0000_0000_01000;  localparam        WIP_DONE        =   13'b0000_0000_10000;  localparam        IDLE            =   13'b0000_0001_00000;  localparam        RDSTATE         =   13'b0000_0010_00000;  localparam        PPWREN          =   13'b0000_0100_00000;  localparam        PPSTATE         =   13'b0000_1000_00000;  localparam        SEWREN          =   13'b0001_0000_00000;  localparam        SESTATE         =   13'b0010_0000_00000;  localparam        BEWREN          =   13'b0100_0000_00000;  localparam        BESTATE         =   13'b1000_0000_00000;    reg               [12:0]      c_state;  reg               [12:0]      n_state;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= INIT_RDSR;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      INIT_RDSR         :   begin        n_state = INIT_RDID;      end            INIT_RDID         :   begin        if (rdsr_done == 1'b1)          n_state = INIT_ID;        else          n_state = INIT_RDID;      end            INIT_ID           :   begin        if (rdid_done == 1'b1)          n_state = WIP;        else          n_state = INIT_ID;      end            WIP               :   begin        n_state = WIP_DONE;      end            WIP_DONE          :   begin        if (rdsr_done == 1'b1)          n_state = IDLE;        else          n_state = WIP_DONE;      end            IDLE              :   begin        if (flag_wr == 1'b1)          n_state = PPWREN;        else          if (flag_rd == 1'b1)            n_state = RDSTATE;          else            if (flag_be == 1'b1)              n_state = BEWREN;            else              if (flag_se == 1'b1)                n_state = SEWREN;              else                n_state = IDLE;      end            RDSTATE           :   begin        if (read_done == 1'b1)          n_state = WIP;        else          n_state = RDSTATE;      end            PPWREN            :   begin        if (wren_done == 1'b1)          n_state = PPSTATE;        else          n_state = PPWREN;      end            PPSTATE           :   begin        if (pp_done == 1'b1)          n_state = WIP;        else          n_state = PPSTATE;      end            SEWREN            :   begin        if (wren_done == 1'b1)          n_state = SESTATE;        else          n_state = SEWREN;      end            SESTATE           :   begin        if (se_done == 1'b1)          n_state = WIP;        else          n_state = SESTATE;      end            BEWREN            :   begin        if (wren_done == 1'b1)          n_state = BESTATE;        else          n_state = BEWREN;      end            BESTATE           :   begin        if (be_done == 1'b1)          n_state = WIP;        else          n_state = BESTATE;      end            default     :   n_state = INIT_RDSR;    endcase  end    assign drive_busy = (c_state != IDLE || flag_be == 1'b1 || flag_rd == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1);    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      spi_cs_n <= 1'b1;    else      case (c_state)        INIT_RDSR       :     spi_cs_n <= 1'b1;                INIT_RDID       :     if (spi_read_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                INIT_ID         :     if (rdid_done == 1'b1)                                  spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                WIP             :     spi_cs_n <= 1'b1;                WIP_DONE        :     if (spi_read_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                                        IDLE            :     spi_cs_n <= 1'b1;                RDSTATE         :     if (read_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                PPWREN          :     if (wren_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                                        PPSTATE         :     if (pp_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;              SEWREN          :     if (wren_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                                        SESTATE         :     if (se_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;              BEWREN          :     if (wren_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                                        BESTATE         :     if (be_done == 1'b1)                                spi_cs_n <= 1'b1;                              else                                spi_cs_n <= 1'b0;                default         :     spi_cs_n <= 1'b1;      endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdsr_en <= 1'b0;    else      if (c_state == INIT_RDSR || c_state == WIP)        rdsr_en <= 1'b1;      else        rdsr_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wren_en <= 1'b0;    else      if (c_state == IDLE && (flag_be == 1'b1 || flag_se == 1'b1 || flag_wr == 1'b1))        wren_en <= 1'b1;      else        wren_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      pp_en <= 1'b0;    else      if (c_state == PPWREN && wren_done == 1'b1)        pp_en <= 1'b1;      else        pp_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_len <= 9'd0;    else      if (c_state == IDLE && flag_wr == 1'b1)        wr_len <= len;      else        wr_len <= wr_len;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wr_addr <= 24'd0;    else      if (c_state == IDLE && flag_wr == 1'b1)        wr_addr <= addr;      else        wr_addr <= wr_addr;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      be_en <= 1'b0;    else      if (c_state == BEWREN && wren_done == 1'b1)        be_en <= 1'b1;      else        be_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      se_en <= 1'b0;    else      if (c_state == SEWREN && wren_done == 1'b1)        se_en <= 1'b1;      else        se_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      se_addr <= 24'd0;    else      if (c_state == IDLE && flag_se == 1'b1)        se_addr <= addr;      else        se_addr <= se_addr;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdid_en <= 1'b0;    else      if (c_state == INIT_RDID && rdsr_done == 1'b1)        rdid_en <= 1'b1;      else        rdid_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_len <= 9'd0;    else      if (c_state == IDLE && flag_rd == 1'b1)        rd_len <= len;      else        rd_len <= rd_len;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rd_addr <= 24'd0;    else      if (c_state == IDLE && flag_rd == 1'b1)        rd_addr <= addr;      else        rd_addr <= rd_addr;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      read_en <= 1'b0;    else      if (c_state == IDLE && flag_rd == 1'b1)        read_en <= 1'b1;      else        read_en <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      mux_sel <= 3'd0;    else      case (c_state)        INIT_RDSR       :   mux_sel <= 3'd0;        INIT_RDID       :   mux_sel <= 3'd0;        INIT_ID         :   mux_sel <= 3'd5;        WIP             :   mux_sel <= 3'd0;        WIP_DONE        :   mux_sel <= 3'd0;        IDLE            :   mux_sel <= 3'd0;        RDSTATE         :   mux_sel <= 3'd6;        PPWREN          :   mux_sel <= 3'd2;        PPSTATE         :   mux_sel <= 3'd1;        SEWREN          :   mux_sel <= 3'd2;        SESTATE         :   mux_sel <= 3'd4;        BEWREN          :   mux_sel <= 3'd2;        BESTATE         :   mux_sel <= 3'd3;        default         :   mux_sel <= 3'd0;      endcase  end  endmodule

m25p16_drive設計實現(xiàn)

本模塊負責連接所有二級模塊,實現(xiàn)所有的功能。

module m25p16_drive (
  input     wire                  clk,  input     wire                  rst_n,    input     wire                  wrfifo_wr,  input     wire    [7:0]         wrfifo_data,    input     wire                  flag_be,  input     wire                  flag_se,  input     wire                  flag_wr,  input     wire                  flag_rd,    input     wire    [23:0]        addr,  input     wire    [8:0]         len,    input     wire                  rdfifo_rd,  output    wire    [7:0]         rdfifo_rdata,  output    wire                  rdfifo_rdempty,    output    wire                  drive_busy,    output    wire                  spi_cs_n,  output    wire                  spi_sclk,  output    wire                  spi_mosi,  input     wire                  spi_miso);
  wire                            spi_send_en;  wire              [7:0]         spi_send_data;  wire                            spi_send_done;  wire                            spi_read_en;  wire              [7:0]         spi_read_data;  wire                            spi_read_done;    wire                            rdsr_send_en;  wire              [7:0]         rdsr_send_data;  wire                            rdsr_read_en;    wire                            pp_send_en;  wire              [7:0]         pp_send_data;    wire                            wren_send_en;  wire              [7:0]         wren_send_data;    wire                            be_send_en;  wire              [7:0]         be_send_data;    wire                            se_send_en;  wire              [7:0]         se_send_data;    wire                            rdid_send_en;  wire              [7:0]         rdid_send_data;  wire                            rdid_read_en;    wire                            read_send_en;  wire              [7:0]         read_send_data;  wire                            read_read_en;    wire              [2:0]         mux_sel;    wire                            be_en;  wire                            be_done;  wire                            wren_en;  wire                            wren_done;  wire                            se_en;  wire              [23:0]        se_addr;  wire                            se_done;  wire                            pp_en;  wire                            pp_done;  wire                            wr_fifo_rd;  wire              [7:0]         wrdata;  wire              [8:0]         wr_len;  wire              [23:0]        wr_addr;  wire                            rdsr_en;  wire                            rdsr_done;  wire                            rdid_en;  wire                            rdid_done;  wire                            read_en;  wire              [23:0]        rd_addr;  wire              [8:0]         rd_len;  wire              [7:0]         rddata;  wire                            rd_fifo_wr;  wire                            read_done;    ctrl ctrl_inst(
      .clk            (clk),      .rst_n          (rst_n),            .flag_be        (flag_be),      .flag_se        (flag_se),      .flag_wr        (flag_wr),      .flag_rd        (flag_rd),      .addr           (addr),      .len            (len),            .drive_busy     (drive_busy),            .spi_cs_n       (spi_cs_n),            .spi_read_done  (spi_read_done),      .rdsr_en        (rdsr_en),      .rdsr_done      (rdsr_done),            .wren_en        (wren_en),      .wren_done      (wren_done),            .pp_en          (pp_en),      .wr_addr        (wr_addr),      .wr_len         (wr_len),      .pp_done        (pp_done),            .be_en          (be_en),      .be_done        (be_done),            .se_en          (se_en),      .se_addr        (se_addr),      .se_done        (se_done),            .rdid_en        (rdid_en),      .rdid_done      (rdid_done),            .read_en        (read_en),      .rd_addr        (rd_addr),      .rd_len         (rd_len),      .read_done      (read_done),            .mux_sel        (mux_sel)    );    rd_fifo  rd_fifo_inst (      .aclr           ( ~rst_n ),      .clock          ( clk ),      .data           ( rddata ),      .rdreq          ( rdfifo_rd ),      .wrreq          ( rd_fifo_wr ),      .empty          ( rdfifo_rdempty ),      .q              ( rdfifo_rdata )    );    wr_fifo  wr_fifo_inst (      .aclr           ( ~rst_n ),      .clock          ( clk ),      .data           ( wrfifo_data ),      .rdreq          ( wr_fifo_rd ),      .wrreq          ( wrfifo_wr ),      .q              ( wrdata )    );    read_ctrl read_ctrl_inst(
      .clk              (clk),      .rst_n            (rst_n),            .read_en          (read_en),      .rd_addr          (rd_addr),      .rd_len           (rd_len),            .rddata           (rddata),      .rd_fifo_wr       (rd_fifo_wr),            .read_done        (read_done),            .read_send_en     (read_send_en),      .read_send_data   (read_send_data),      .spi_send_done    (spi_send_done),            .read_read_en     (read_read_en),      .spi_read_done    (spi_read_done),      .spi_read_data    (spi_read_data)    );
  rdid rdid_inst(
      .clk              (clk),      .rst_n            (rst_n),            .rdid_en          (rdid_en),      .rdid_done        (rdid_done),            .rdid_send_en     (rdid_send_en),      .rdid_send_data   (rdid_send_data),      .spi_send_done    (spi_send_done),            .rdid_read_en     (rdid_read_en),      .spi_read_done    (spi_read_done),      .spi_read_data    (spi_read_data)    );      rdsr rdsr_inst(
      .clk              (clk),      .rst_n            (rst_n),            .rdsr_en          (rdsr_en),      .rdsr_done        (rdsr_done),            .rdsr_send_en     (rdsr_send_en),      .rdsr_send_data   (rdsr_send_data),      .spi_send_done    (spi_send_done),            .rdsr_read_en     (rdsr_read_en),      .spi_read_data    (spi_read_data),      .spi_read_done    (spi_read_done)    );    pp pp_inst(
      .clk              (clk),      .rst_n            (rst_n),            .pp_en            (pp_en),      .pp_done          (pp_done),      .wr_fifo_rd       (wr_fifo_rd),      .wrdata           (wrdata),      .wr_len           (wr_len),      .wr_addr          (wr_addr),            .pp_send_en       (pp_send_en),      .pp_send_data     (pp_send_data),      .spi_send_done    (spi_send_done)    );    se se_inst(
      .clk              (clk),      .rst_n            (rst_n),            .se_en            (se_en),      .se_addr          (se_addr),      .se_done          (se_done),            .se_send_en       (se_send_en),      .se_send_data     (se_send_data),      .spi_send_done    (spi_send_done)    );    wren wren_inst(
      .clk              (clk),      .rst_n            (rst_n),            .wren_en          (wren_en),      .wren_done        (wren_done),            .wren_send_en     (wren_send_en),      .wren_send_data   (wren_send_data),      .spi_send_done    (spi_send_done)    );
  be be_inst(
      .clk              (clk),      .rst_n            (rst_n),            .be_en            (be_en),      .be_done          (be_done),            .be_send_en       (be_send_en),      .be_send_data     (be_send_data),      .spi_send_done    (spi_send_done)    );    mux7_1 mux7_1_inst(
      .rdsr_send_en     (rdsr_send_en),      .rdsr_send_data   (rdsr_send_data),      .rdsr_read_en     (rdsr_read_en),            .pp_send_en       (pp_send_en),      .pp_send_data     (pp_send_data),            .wren_send_en     (wren_send_en),      .wren_send_data   (wren_send_data),            .be_send_en       (be_send_en),      .be_send_data     (be_send_data),            .se_send_en       (se_send_en),      .se_send_data     (se_send_data),            .rdid_send_en     (rdid_send_en),      .rdid_send_data   (rdid_send_data),      .rdid_read_en     (rdid_read_en),            .read_send_en     (read_send_en),      .read_send_data   (read_send_data),      .read_read_en     (read_read_en),            .mux_sel          (mux_sel),            .spi_send_en      (spi_send_en),      .spi_send_data    (spi_send_data),      .spi_read_en      (spi_read_en)    );    spi_8bit_drive spi_8bit_drive_inst(
      .clk              (clk),      .rst_n            (rst_n),            .spi_send_en      (spi_send_en),      .spi_send_data    (spi_send_data),      .spi_send_done    (spi_send_done),            .spi_read_en      (spi_read_en),      .spi_read_data    (spi_read_data),      .spi_read_done    (spi_read_done),            .spi_sclk         (spi_sclk),      .spi_mosi         (spi_mosi),      .spi_miso         (spi_miso)  );  endmodule

RTL仿真

本次設計涉及到讀取flash的id以及狀態(tài)寄存器,所以在仿真時需要加入仿真模型。仿真模型放在msim的m25p16_sim_module中。m25p16為仿真模型的頂層文件。

由于讀寫和擦除的時間較長,RTL仿真中,將只仿真RDSR和RDID,其他的功能測試在板級測試時進行。

仿真代碼如下:

`timescale 1ns/1ps
module m25p16_drive_tb;
  reg             clk;  reg             rst_n;    wire            drive_busy;    wire            spi_cs_n;  wire            spi_sclk;  wire            spi_mosi;  wire            spi_miso;
  m25p16_drive m25p16_drive_inst(
      .clk              (clk),      .rst_n            (rst_n),            .wrfifo_wr        (1'b0),      .wrfifo_data      (8'd0),            .flag_be          (1'b0),      .flag_se          (1'b0),      .flag_wr          (1'b0),      .flag_rd          (1'b0),            .addr             (24'd0),      .len              (9'd0),            .rdfifo_rd        (1'b0),      .rdfifo_rdata     (),      .rdfifo_rdempty   (),            .drive_busy       (drive_busy),            .spi_cs_n         (spi_cs_n),      .spi_sclk         (spi_sclk),      .spi_mosi         (spi_mosi),      .spi_miso         (spi_miso)    );
  m25p16 m25p16_inst(      .c                (spi_sclk),      .data_in          (spi_mosi),      .s                (spi_cs_n),      .w                (1'b1),      .hold             (1'b1),      .data_out         (spi_miso)    );
  initial clk = 1'b0;  always # 50 clk = ~clk;    initial begin    rst_n = 1'b0;    # 201    rst_n = 1'b1;    @ (negedge drive_busy);    # 2000    $stop;  end
endmodule

在設置testbench時,注意將所有文件全部添加到文件中。

選擇testbench時,注意選中設置的m25p16_drive_tb。

利用modelsim仿真,可以得出如下RTL仿真波形。

讀到ID,以及檢測WIP都是正確的。

板級測試

由于m25p16的時序原因,整個設計工作在10MHz(利用PLL產生)。

在進行測試控制時,對最后一個扇區(qū)進行擦除;對最后一個扇區(qū)的第一頁進行寫入數(shù)據(jù)100個(1至100);對最后一個扇區(qū)的第一個進行讀取,驗證數(shù)據(jù)是否為1至100。

測試的控制模塊命名為test_ctrl。

此模塊采用狀態(tài)機實現(xiàn)。WRFIFO(將1至100寫入wrfifo中)、SE(扇區(qū)擦除)、PP(寫入flash)、RD(讀出flash)、WAIT_RD(等待讀?。?、CHECK( 檢測讀出的數(shù)據(jù)的正確性)。

設計代碼為:

module test_ctrl (
  input     wire                  clk,  input     wire                  rst_n,    output    reg                   wrfifo_wr,  output    reg     [7:0]         wrfifo_data,    output    reg                   flag_se,  output    reg                   flag_wr,  output    reg                   flag_rd,    input     wire                  drive_busy,    output    reg                   rdfifo_rd,  input     wire    [7:0]         rdfifo_rdata);
  localparam      WRFIFO      =     6'b000_001;  localparam      SE          =     6'b000_010;  localparam      PP          =     6'b000_100;  localparam      RD          =     6'b001_000;  localparam      WAIT_RD     =     6'b010_000;  localparam      CHECK       =     6'b100_000;    reg               [5:0]         c_state;  reg               [5:0]         n_state;    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      c_state <= WRFIFO;    else      c_state <= n_state;  end    always @ * begin    case (c_state)      WRFIFO        :   begin        if (wrfifo_data == 8'd100)          n_state = SE;        else          n_state = WRFIFO;      end            SE            :   begin        if (drive_busy == 1'b0)          n_state = PP;        else          n_state = SE;      end            PP            :   begin        if (drive_busy == 1'b0)          n_state = RD;        else          n_state = PP;      end            RD            :   begin        if (drive_busy == 1'b0)          n_state = WAIT_RD;        else          n_state = RD;      end          WAIT_RD       :   begin        if (drive_busy == 1'b0)          n_state = CHECK;        else          n_state = WAIT_RD;      end
      CHECK         :   begin        n_state = CHECK;      end
      default       :   n_state = WRFIFO;          endcase  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wrfifo_data <= 8'd0;    else      if (c_state == WRFIFO && wrfifo_data < 8'd100)        wrfifo_data <= wrfifo_data + 1'b1;      else        wrfifo_data <= 8'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      wrfifo_wr <= 1'd0;    else      if (c_state == WRFIFO && wrfifo_data < 8'd100)        wrfifo_wr <= 1'd1;      else        wrfifo_wr <= 1'd0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      flag_se <= 1'b0;    else      if (c_state == SE && drive_busy == 1'b0)        flag_se <= 1'b1;      else        flag_se <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      flag_wr <= 1'b0;    else      if (c_state == PP && drive_busy == 1'b0)        flag_wr <= 1'b1;      else          flag_wr <= 1'b0;  end
  always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      flag_rd <= 1'b0;    else      if (c_state == RD && drive_busy == 1'b0)        flag_rd <= 1'b1;      else        flag_rd <= 1'b0;  end    always @ (posedge clk, negedge rst_n) begin    if (rst_n == 1'b0)      rdfifo_rd <= 1'b0;    else      if (c_state == WAIT_RD && drive_busy == 1'b0)        rdfifo_rd <= 1'b1;      else        if (c_state == CHECK && rdfifo_rdata == 8'd99)          rdfifo_rd <= 1'b0;        else            rdfifo_rd <= rdfifo_rd;  end  endmodule

將test模塊設置為頂層。在test模塊中,m25p16_drive例化中,對于整片擦除不做控制,對于addr直接指向最后一個扇區(qū)的第一頁,len指定為100。

代碼為:

module test (
  input     wire                  clk,  input     wire                  rst_n,    output    wire                  spi_cs_n,  output    wire                  spi_sclk,  output    wire                  spi_mosi,  input     wire                  spi_miso);
  wire                            wrfifo_wr;  wire              [7:0]         wrfifo_data;  wire                            flag_rd;  wire                            flag_se;  wire                            flag_wr;  wire                            drive_busy;  wire                            rdfifo_rd;  wire              [7:0]         rdfifo_rdata;    wire                            clk_10m;  wire                            pll_locked;    pll_test  pll_test_inst (      .areset             ( ~rst_n ),      .inclk0             ( clk ),      .c0                 ( clk_10m ),      .locked             ( pll_locked )    );
  test_ctrl test_ctrl_inst(
      .clk                (clk_10m),      .rst_n              (pll_locked),            .wrfifo_wr          (wrfifo_wr),      .wrfifo_data        (wrfifo_data),            .flag_se            (flag_se),      .flag_wr            (flag_wr),      .flag_rd            (flag_rd),            .drive_busy         (drive_busy),            .rdfifo_rd          (rdfifo_rd),      .rdfifo_rdata       (rdfifo_rdata)    );      m25p16_drive m25p16_drive_inst(
      .clk              (clk_10m),      .rst_n            (pll_locked),            .wrfifo_wr        (wrfifo_wr),      .wrfifo_data      (wrfifo_data),            .flag_be          (1'b0),      .flag_se          (flag_se),      .flag_wr          (flag_wr),      .flag_rd          (flag_rd),            .addr             (24'hff0000),      .len              (9'd100),            .rdfifo_rd        (rdfifo_rd),      .rdfifo_rdata     (rdfifo_rdata),      .rdfifo_rdempty   (),            .drive_busy       (drive_busy),            .spi_cs_n         (spi_cs_n),      .spi_sclk         (spi_sclk),      .spi_mosi         (spi_mosi),      .spi_miso         (spi_miso)    );  endmodule

由于開發(fā)板上的flash是為FPGA進行保存配置信息的,所以管腳都連接在專用管腳上,本次實驗需要將這專用管腳配置為普通io。

右擊器件型號,選擇device。

點擊device and pin options。

選擇Dual-purpose pins,將其中所有的功能改為普通IO。

點擊ok后,即可進行綜合分析。

連接開發(fā)板和PC,打開邏輯分析儀。

采樣時鐘選擇10MHz(PLL 的c0),采樣深度設置為2K。

觀測信號如下圖所示。

首先將wrfifo_wr的觸發(fā)條件設置為上升沿。點擊觸發(fā)后,按下復位按鍵。觸發(fā)后,可以看到寫入數(shù)據(jù)1至100后,然后進行SE命令。

將rdfifo_rd的觸發(fā)條件設置為上升沿(將wrfifo_wr觸發(fā)條件修改為donot care)。點擊觸發(fā)后,按下復位按鍵。

通過仿真和下板實測,驗證控制器設計正確。

 

 

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
XC7A15T-2CSG324I 1 AMD Xilinx Field Programmable Gate Array, 1300 CLBs, PBGA324, BGA-324

ECAD模型

下載ECAD模型
$138.91 查看
XC6SLX9-2FTG256C 1 AMD Xilinx Field Programmable Gate Array, 715 CLBs, 667MHz, 9152-Cell, CMOS, PBGA256, 17 X 17 MM, 1 MM PITCH, LEAD FREE, FBGA-256

ECAD模型

下載ECAD模型
$28.38 查看
EP2C35F484I8N 1 Altera Corporation Field Programmable Gate Array, 2076 CLBs, 402.5MHz, 33216-Cell, CMOS, PBGA484, LEAD FREE, FBGA-484
$337.2 查看

相關推薦

電子產業(yè)圖譜

任何技術的學習就好比一個江湖,對于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。