试想这样一种场景,有两款不同的FPGA板卡,它们的功能代码90%都是一样的,但是两个板卡的管脚分配完全不同,一般情况下,我们需要设计两个工程,两套代码,之后还需要一直维护两个版本。 那么有没有一种自动化的方式,实现一个工程,编译出一个程序文件,下载到这两个不同的板卡上,都可以正常运行呢?
本文以开发板A和开发板B为例,介绍如何实现一套FPGA工程无缝兼容两款管脚不同的板卡?
两款开发板的时钟信号分别为clk_a和clk_b,分别位于两个不同的芯片管脚,两个开发板的FPGA型号完全一致,外部时钟的频率也一样。
首先需要判断当前是哪款板卡?实现方式是通过两个计数器,分别对时钟信号进行计数,由于两款板子的时钟信号分别位于不同的管脚,所以只有一个计数器会累加,并达到目标值,这样就实现了板卡型号的自动区分。
具体代码如下:
/*********************************************************************
  • * Copyright © blog.csdn.net/whik1194
  • * ModuleName : board_sel.v
  • * CreateTim : 2023年11月5日 19:16:48
  • * Author : mcu149
  • * Function : function
  • * Version : v1.0
  • * Version | Modify
  • *
  • ----------------------------------
  • * v1.0 | first version
  • *********************************************************************/
  • module board_sel(
  • //Inputs
  • input clk_a, //100MHz
  • input clk_b, //100MHz
  • //Outputs
  • output reg [1:0] sel = 2'd0,
  • output reg rst_n = 1'b0
  • );
  • //1.parameter
  • // parameter LATCH_TIME = 10_000_000 / 10; //10ms
  • // parameter RESET_TIME = 100_000_000 / 10; //100ms
  • // for simulation
  • parameter LATCH_TIME = 5_000 / 10; //simulation, 5 us
  • parameter RESET_TIME = 10_000 / 10; //simulation, 10 us
  • //2.localparam
  • localparam BOARD_A = 2'd1;
  • localparam BOARD_B = 2'd2;
  • //3.reg
  • reg [31:0] cnt_a = 0;
  • reg [31:0] cnt_b = 0;
  • //4.wire
  • //5.assign
  • //6.always
  • always @ (posedge clk_a) begin
  • if(cnt_a < LATCH_TIME + RESET_TIME)
  • cnt_a <= cnt_a + 1;
  • end
  • always @ (posedge clk_b) begin
  • if(cnt_b < LATCH_TIME + RESET_TIME)
  • cnt_b <= cnt_b + 1;
  • end
  • always @ (*) begin
  • if(cnt_a == LATCH_TIME)
  • sel <= BOARD_A;
  • else if(cnt_b == LATCH_TIME)
  • sel <= BOARD_B;
  • end
  • always @ (*) begin
  • if((cnt_a == LATCH_TIME + RESET_TIME) || (cnt_b == LATCH_TIME + RESET_TIME))
  • rst_n <= 1;
  • end
  • //7.instance
  • endmodule //board_sel end
  • 复制代码

    这里的代码,使用了寄存器定义时赋初值0的一个小技巧,一般工程不建议这么使用。
    板卡区分之后,再根据区分的结果即sel的值对输出、输入分别进行选择。
    具体实现如下:
    /*********************************************************************
  • * Copyright © blog.csdn.net/whik1194
  • * ModuleName : board_sel.v
  • * CreateTim : 2023年11月5日 19:40:48
  • * Author : mcu149
  • * Function : function
  • * Version : v1.0
  • * Version | Modify
  • *
  • ----------------------------------
  • * v1.0 | first version
  • *********************************************************************/
  • module board_dock(
  • //Inputs
  • input [1:0] sel,
  • input clk_a,
  • input clk_b,
  • input uart_txd,
  • input led1,
  • //Outputs
  • output clk,
  • output uart_txd_a,
  • output uart_txd_b,
  • output led1_a,
  • output led1_b
  • );
  • //1.parameter
  • //2.localparam
  • localparam BOARD_A = 2'd1;
  • localparam BOARD_B = 2'd2;
  • localparam DEFAULT_OUT_VALUE = 1'b1;
  • //3.reg
  • //4.wire
  • //5.assign
  • assign clk = (sel == 2'd0 ) ? DEFAULT_OUT_VALUE : ((sel == BOARD_B) ? clk_b : clk_a );
  • assign uart_txd_a = (sel == BOARD_A) ? uart_txd : DEFAULT_OUT_VALUE;
  • assign uart_txd_b = (sel == BOARD_B) ? uart_txd : DEFAULT_OUT_VALUE;
  • assign led1_a = (sel == BOARD_A) ? led1 : DEFAULT_OUT_VALUE;
  • assign led1_b = (sel == BOARD_B) ? led1 : DEFAULT_OUT_VALUE;
  • //6.always
  • //7.instance
  • endmodule //board_dock end

  • 仿真文件:

  • `timescale 1ns/1ps
  • `define BRD_A
  • // `define BRD_B
  • module top_tb;
  • localparam PERIOD = 10; //10ns
  • localparam BOARD_A = 2'd1;
  • localparam BOARD_B = 2'd2;
  • reg clk_a;
  • reg clk_b;
  • reg uart_txd;
  • reg led1;
  • wire [1:0] sel;
  • wire rst_n;
  • `ifdef BRD_A
  • always #(PERIOD/2) clk_a <= !clk_a;
  • `endif
  • `ifdef BRD_B
  • always #(PERIOD/2) clk_b <= !clk_b;
  • `endif
  • initial begin
  • $display("testbench: %s", "top_tb");
  • clk_a = 0;
  • clk_b = 0;
  • uart_txd = 0;
  • led1 = 0;
  • end
  • always #(500_000) uart_txd <= !uart_txd;
  • always #(200_000) led1 <= !led1;
  • board_sel board_sel_ut0(
  • //Inputs
  • .clk_a(clk_a), //100MHz
  • .clk_b(clk_b), //100MHz
  • //Outputs
  • .sel(sel),
  • .rst_n(rst_n)
  • );
  • board_dock board_dock_ut0(
  • //Inputs
  • .sel(sel),
  • .clk_a(clk_a),
  • .clk_b(clk_b),
  • .uart_txd(uart_txd),
  • .led1(led1),
  • //Outputs
  • .clk(clk),
  • .uart_txd_a(uart_txd_a),
  • .uart_txd_b(uart_txd_b),
  • .led1_a(led1_a),
  • .led1_b(led1_b)
  • );
  • endmodule //top_tb end
  • 复制代码
    总结
    本文所提出的方式,可以在某些应用场景对板卡实现一定的兼容性,比如用来固件在线升级所使用的Golden镜像工程,不同的板子共用此工程,以后只需要维护一套代码即可。


    当然这种方式也有一定的局限性,比如需要两款板卡的FPGA芯片型号一致、晶振频率一致,比如同样为XC7K325T,外部输入单端50M时钟。