【方法】Altera Cyclone系列的EP1C3T144C8N FPGA利用ALTASMI_PARALLEL IP核读写自身的EPCS Flash存储器

Altera FPGA的程序是存储在外部的EPCS Flash里面的。EP1C3T144C8N的程序大小只有78422字节(76.5KB),剩余Flash空间可以用来存储其他数据。
利用Serial Flash Loader IP核,Verilog程序可以直接操作EPCS SPI Flash的片选、时钟和数据引脚,从而读写Flash数据。SPI通信协议和普通的SPI Flash(如W25Q128)差不多。
不过还有更简单的方法,Quartus里面还有一个名叫ALTASMI_PARALLEL的IP核。利用这个IP核,可以不需要实现SPI协议,直接读写SPI Flash的数据。

下面我们来尝试读取一下存储在EPCS16 SPI Flash内的前256字节数据:

module main(
    input clock,
    output uart_tx
    );
    
    localparam CLK_IN_FREQ = 48000000;
    
    /* 产生复位信号 */
    wire nrst;
    Reset reset(clock, nrst);
    
    /* 串口发送 */
    wire uart_tx_request;
    wire [7:0] uart_tx_data;
    wire uart_tx_ready;
    wire uart_sent;
    UARTTransmitter #(CLK_IN_FREQ) uart_transmitter(clock, nrst, uart_tx, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent);
    
    localparam UART_TX_MAXSIZE = 8'd32;
    reg [UART_TX_MAXSIZE * 8 - 1:0] uart_bytearray_tx_data;
    reg [1:0] uart_bytearray_tx_mode;
    reg uart_bytearray_tx_request;
    reg [7:0] uart_bytearray_tx_size;
    reg uart_bytearray_tx_endl;
    wire uart_bytearray_tx_ready;
    wire uart_bytearray_sent;
    ByteArrayTransmitter #(UART_TX_MAXSIZE) uart_bytearray_transmitter(clock, nrst, uart_tx_request, uart_tx_data, uart_tx_ready, uart_sent, 
      uart_bytearray_tx_mode, uart_bytearray_tx_request, uart_bytearray_tx_data, uart_bytearray_tx_size, uart_bytearray_tx_endl, 
      uart_bytearray_tx_ready, uart_bytearray_sent);
    
    /* EPCS Flash 读写 */
    wire altasmi_clkin;
    ClockDivider #(2) clock_divider(clock, nrst, altasmi_clkin);
    reg [23:0] altasmi_addr;
    reg altasmi_rden;
    reg altasmi_read;
    reg altasmi_read_rdid;
    reg altasmi_read_sid;
    wire altasmi_busy;
    wire altasmi_data_valid;
    wire [7:0] altasmi_dataout;
    wire [7:0] altasmi_epcs_id;
    wire [7:0] altasmi_rdid_out;
    wire [23:0] altasmi_read_address;
    
    ALTASMI_PARALLEL_0 ALTASMI_PARALLEL_0(
        .addr(altasmi_addr),
        .clkin(~altasmi_clkin),
        .rden(altasmi_rden),
        .read(altasmi_read),
        .read_rdid(altasmi_read_rdid),
        .read_sid(altasmi_read_sid),
        .reset(~nrst),
        .busy(altasmi_busy),
        .data_valid(altasmi_data_valid),
        .dataout(altasmi_dataout),
        .epcs_id(altasmi_epcs_id),
        .rdid_out(altasmi_rdid_out),
        .read_address(altasmi_read_address)
	);
    
    // 交换一下位的顺序, 变成和jic文件里面一样的顺序
    // 注意: 编译程序, 生成*.jic文件, 用USB-Blaster固化到Flash后, 运行时串口里面的打印才和当前程序的jic文件内容一样
    // 不要去和rbf文件比较!rbf文件的内容和jic里面表示的Flash内容是不一样的!
    reg [7:0] altasmi_dataout_swapped;
    reg [3:0] altasmi_i;
    always @(*) begin
        for (altasmi_i = 0; altasmi_i < 8; altasmi_i = altasmi_i + 1'b1)
            altasmi_dataout_swapped[altasmi_i] = altasmi_dataout[7 - altasmi_i];
    end
    
    // 十六进制数转字符串
    function reg [15:0] hex2str(input [7:0] num);
        begin
            if (num[7:4] <= 9)
                hex2str[15:8] = "0" + num[7:4];
            else
                hex2str[15:8] = "a" + num[7:4] - 4'd10;
            if (num[3:0] <= 9)
                hex2str[7:0] = "0" + num[3:0];
            else
                hex2str[7:0] = "a" + num[3:0] - 4'd10;
        end
    endfunction
    
    localparam STATE_EPCS_REQ = 0;
    localparam STATE_EPCS_WAIT = 1;
    localparam STATE_UART_REQ = 2;
    localparam STATE_UART_WAIT = 3;
    reg [7:0] delay;
    reg [7:0] i;
    reg [7:0] j;
    reg k;
    reg [1:0] state;
    always @(posedge clock, negedge nrst) begin
        if (!nrst) begin
            altasmi_addr <= 0;
            altasmi_rden <= 0;
            altasmi_read <= 0;
            altasmi_read_rdid <= 0;
            altasmi_read_sid <= 0;
            uart_bytearray_tx_request <= 0;
            
            delay <= 10;
            i <= 0;
            j <= 0;
            k <= 0;
            state <= STATE_EPCS_REQ;
        end
        else if (delay != 0)
            delay <= delay - 1'b1;
        else begin
            case (state)
                STATE_EPCS_REQ: begin
                    case (i)
                        0: begin
                            // 读ID
                            if (!altasmi_read_rdid)
                                altasmi_read_rdid <= 1;
                            else if (altasmi_busy) begin
                                altasmi_read_rdid <= 0;
                                state <= STATE_EPCS_WAIT;
                            end
                        end
                        1: begin
                            // 读SID
                            if (!altasmi_read_sid)
                                altasmi_read_sid <= 1;
                            else if (altasmi_busy) begin
                                altasmi_read_sid <= 0;
                                state <= STATE_EPCS_WAIT;
                            end
                        end
                        2: begin
                            // 读一页 (256字节)
                            if (!altasmi_rden) begin
                                altasmi_read <= 1;
                                altasmi_rden <= 1;
                            end
                            else if (altasmi_busy) begin
                                altasmi_read <= 0;
                                if (!k) begin
                                    if (altasmi_data_valid) begin
                                        uart_bytearray_tx_data[{UART_TX_MAXSIZE - j - 1'b1, 3'b0}+:8] <= altasmi_dataout_swapped;
                                        if (j != UART_TX_MAXSIZE - 1'b1) begin
                                            j <= j + 1'b1;
                                            k <= 1; // 避免同一个data_valid脉冲触发多次读取
                                        end
                                        else begin
                                            j <= 0;
                                            altasmi_rden <= 0;
                                            altasmi_addr <= altasmi_addr + UART_TX_MAXSIZE;
                                            state <= STATE_EPCS_WAIT; // 等待busy信号撤销
                                        end
                                    end
                                end
                                else begin
                                    if (!altasmi_data_valid)
                                        k <= 0;
                                end
                            end
                        end
                    endcase
                end
                STATE_EPCS_WAIT: begin
                    case (i)
                        0: begin
                            if (!altasmi_busy) begin
                                uart_bytearray_tx_mode <= 1;
                                uart_bytearray_tx_data <= {"ID: 0x", hex2str(altasmi_rdid_out)};
                                uart_bytearray_tx_size <= UART_TX_MAXSIZE;
                                uart_bytearray_tx_endl <= 1;
                                i <= 1;
                                state <= STATE_UART_REQ;
                            end
                        end
                        1: begin
                            if (!altasmi_busy) begin
                                uart_bytearray_tx_mode <= 1;
                                uart_bytearray_tx_data <= {"SID: 0x", hex2str(altasmi_epcs_id)};
                                uart_bytearray_tx_size <= UART_TX_MAXSIZE;
                                uart_bytearray_tx_endl <= 1;
                                i <= 2;
                                state <= STATE_UART_REQ;
                            end
                        end
                        2: begin
                            if (!altasmi_busy) begin
                                uart_bytearray_tx_mode <= 2;
                                uart_bytearray_tx_size <= UART_TX_MAXSIZE;
                                state <= STATE_UART_REQ;
                                if (altasmi_addr == 256) begin
                                    uart_bytearray_tx_endl <= 1;
                                    altasmi_addr <= 0;
                                    i <= 3;
                                end
                                else
                                    uart_bytearray_tx_endl <= 0;
                            end
                        end
                    endcase
                end
                STATE_UART_REQ: begin
                    if (uart_bytearray_tx_ready) begin
                        if (!uart_bytearray_tx_request)
                            uart_bytearray_tx_request <= 1;
                    end
                    else
                        state <= STATE_UART_WAIT;
                end
                STATE_UART_WAIT: begin
                    if (uart_bytearray_tx_ready && uart_bytearray_tx_request)
                        uart_bytearray_tx_request <= 0; // 关闭发送请求
                    else if (uart_bytearray_sent)
                        state <= STATE_EPCS_REQ; // 发送完毕
                end
            endcase
        end
    end

endmodule
module Reset(
    input clock,
    output nrst
    );
    
    reg [3:0] counter = 4'd15;
    assign nrst = (counter == 0);
    always @(posedge clock) begin
        if (!nrst)
            counter <= counter - 1'b1;
    end
    
endmodule
module ClockDivider #(
    parameter N = 2
    )(
    input clk_in,
    input nrst,
    output reg clk_out
    );
    
    function integer clogb2(input integer depth);
        for (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1)
            depth = depth >> 1;
    endfunction
    
    reg [clogb2(N - 1) - 1:0] counter;
    always @(posedge clk_in, negedge nrst) begin
        if (!nrst) begin
            clk_out <= 0;
            counter <= 0;
        end
        else if (counter == N - 1) begin
            counter <= 0;
            clk_out <= ~clk_out;
        end
        else
            counter <= counter + 1'b1;
    end
    
endmodule
module UARTTransmitter #(
    parameter SYSCLK = 50000000,
    parameter BAUDRATE = 115200
    )(
    input clock, // 系统时钟
    input nrst, // 模块复位
    output reg tx, // 串口发送引脚
    input request, // 请求发送字符 (ready=1后应该及时撤销请求, 否则会再次发送同样的字符)
    input [7:0] data, // 发送的字符内容 (ready=0时必须保持不变)
    output ready, // 是否可以送入新字符
    output sent // 是否发送完毕
    );
    
    integer counter; // 波特率计数器
    reg [3:0] bit_i; // 当前正在发送第几位 (0为起始位, 1-8为数据位, 9为停止位, 10为空闲)
    wire bit_start = (counter == 0); // 位开始信号
    //wire bit_sample = (counter == SYSCLK / BAUDRATE / 2 - 1); // 接收端采样点信号
    wire bit_end = (counter == SYSCLK / BAUDRATE - 1); // 位结束信号
    
    /*
    ready引脚的真值表:
    bit_i   request    |  ready
    -------------------------------------------------------------
    0-8        X       |   0    (正在发送起始位和数据位)
     9         X       |   1    (正在发送停止位, 允许送入新字符)
    10         0       |   1    (空闲, 允许送入新字符)
    10         1       |   0    (已请求发送字符, 但还没开始发送)
    */
    assign ready = (bit_i == 9 || sent);
    assign sent = (bit_i == 10 && !request);
    
    always @(posedge clock, negedge nrst) begin
        if (!nrst) begin
            tx <= 1;
            counter <= 0;
            bit_i <= 10;
        end
        else begin
            if (bit_i <= 9) begin
                if (bit_start) begin
                    counter <= 1;
                    if (bit_i == 0)
                        tx <= 0; // 起始位
                    else if (bit_i >= 1 && bit_i <= 8)
                        tx <= data[bit_i - 1]; // 数据位
                    else
                        tx <= 1; // 停止位
                end
                else if (bit_end) begin
                    counter <= 0;
                    if (bit_i == 9 && request)
                        bit_i <= 0; // 继续发送下一字符, 中间无停顿
                    else
                        bit_i <= bit_i + 1'b1; // 继续发送下一位, 或停止发送
                end
                else
                    counter <= counter + 1;
            end
            else if (request)
                bit_i <= 0; // 开始发送
            else
                counter <= 0; // 空闲
        end
    end
    
endmodule
`define BAT_MAXBIT (MAXSIZE * 8 - 1)

/*
** mode=0: 二进制模式
**         输出数据个数为size
** mode=1: 二进制模式 (用于发送字符串)
**         传输时跳过所有'\0'字符, 输出数据个数小于或等于size
** mode=2: 十六进制字符串模式
**         将输入数据以十六进制字符串格式传输, 输出字符个数为2*size
** mode=3: 十六进制字符串模式 (忽略0x00)
**         传输时跳过所有为零的字节, 输出字符个数小于或等于2*size
*/
module ByteArrayTransmitter #(
    parameter MAXSIZE = 16
    )(
    input clock,
    input nrst,
    output reg byte_request,
    output reg [7:0] byte_out,
    input byte_ready,
    input byte_sent,
    input [1:0] mode, // 0:二进制模式, 1:字符串模式, 2: 十六进制字符串模式
    input request,
    input [`BAT_MAXBIT:0] data,
    input [7:0] size,
    input endl, // 是否在末尾加上"\r\n"
    output ready,
    output sent
    );
    // 当ready为高时, 允许模块外改变请求信号 (也就是mode, data和size这三项数据)
    // 发送过程中, ready拉高后, 本模块就停止工作了 (count已清零)
    // 而且ready比byte_ready后拉高, byte_ready为高时byte_out信号也可以撤销
    // 所以不需要缓存mode, data和size输入信号
    
    localparam STATE_LOAD = 0;
    localparam STATE_CHECK = 1;
    localparam STATE_REQUESTED = 2;
    localparam STATE_SENDING = 3;
    
    reg [7:0] count;
    reg [1:0] ending;
    assign ready = (count == 0 && ending == 0 && ((byte_ready && !byte_sent) || sent));
    assign sent = (byte_sent && !request);
    
    reg [7:0] data_c;
    reg i;
    always @(*) begin
        case (ending)
            0:
                data_c = data[{size - count, 3'b0}+:8];
            1:
                data_c = 8'h0d;
            2:
                data_c = 8'h0a;
            3:
                data_c = 8'h00;
        endcase
        
        if (ending == 0) begin
            case (mode)
                0, 1:
                    byte_out = data_c;
                2, 3: begin
                    byte_out = (i) ? data_c[3:0] : data_c[7:4];
                    if (byte_out <= 9)
                        byte_out = byte_out + "0";
                    else
                        byte_out = byte_out - 8'd10 + "A";
                end
            endcase
        end
        else
            byte_out = data_c;
    end
    
    reg [1:0] state;
    always @(posedge clock, negedge nrst) begin
        if (!nrst) begin
            byte_request <= 0;
            count <= 0;
            ending <= 0;
            state <= STATE_LOAD;
        end
        else begin
            case (state)
                STATE_LOAD: begin
                    if (byte_ready) begin
                        if (count == 0 && ending == 0) begin
                            if (byte_sent && request) begin
                                // 开始发送
                                i <= 0;
                                if (size > 0 && size <= MAXSIZE) begin
                                    // 发送数据
                                    count <= 1;
                                    state <= STATE_CHECK;
                                end
                                else if (size == 0 && endl) begin
                                    // 只发送换行符
                                    ending <= 1;
                                    state <= STATE_CHECK;
                                end
                            end
                        end
                        else begin
                            // 继续发送
                            if ((mode == 2 || mode == 3) && ending == 0 && i == 0) begin
                                // 继续发送另一个十六进制字符
                                i <= 1;
                                state <= STATE_CHECK;
                            end
                            else begin
                                i <= 0;
                                if (ending == 1) begin
                                    // 继续发送换行符
                                    ending <= 2;
                                    state <= STATE_CHECK;
                                end
                                else if (count == size) begin
                                    // 数据输入结束, 但发送还没有结束
                                    if (endl && ending == 0) begin
                                        // 开始发送换行符
                                        ending <= 1;
                                        state <= STATE_CHECK;
                                    end
                                    else begin
                                        // 发送结束, 拉高ready信号
                                        count <= 0;
                                        ending <= 0;
                                    end
                                end
                                else begin
                                    // 继续发送下一个字节
                                    count <= count + 1'b1;
                                    state <= STATE_CHECK;
                                end
                            end
                        end
                    end
                end
                STATE_CHECK: begin
                    // 判断是否需要跳过零字符
                    if ((mode == 1 || mode == 3) && data_c == 0)
                        state <= STATE_LOAD;
                    else begin
                        byte_request <= 1;
                        state <= STATE_REQUESTED;
                    end
                end
                STATE_REQUESTED: begin
                    // 等待发送开始
                    if (!byte_ready)
                        state <= STATE_SENDING;
                end
                STATE_SENDING: begin
                    // 等待发送结束
                    if (byte_ready) begin
                        byte_request <= 0;
                        state <= STATE_LOAD;
                    end
                end
            endcase
        end
    end
    
endmodule

程序编译完成后,生成jic文件,并固化到Flash中。然后运行程序,程序的运行结果如下:

可以看到,EPCS16的Silicon ID为0x14,读出来的前256字节的数据和jic文件里面的一模一样。(注意程序里面做了位翻转)

值得注意的是,不要去和rbf文件比较。rbf文件的内容和jic文件的内容通常是不一样的。我们用USB-Blaster下载器下载进去的是jic文件,应该用jic文件去比较。
甚至,编译时自动生成的rbf文件(可在Device and Pin Options对话框里面勾选)的内容和Convert Programming Files菜单命令里面Generate的rbf文件内容也不一样!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值