在线MakerChip虚拟FPGA设计动态仿真,本己有之,也很成熟。但随着MakerChip的M4到M5升级,Virtual FPGA Lab支撑动态平台的变迁,原有的案例,一个也动弹不了了。登场教培学员之际,几经摸索,终于有了结果,重新整理出来,供业界参考。多亏现代AI工具IMA-Copilot特别是龙虾机器人工具MuleRun助力,一并说明。先看动态组合工效,这是其中的按键操控。

1 LED计数器
1.1 RT Verilog编码
16位二进制计数器,输出显示在模拟Zedboard板的LED上。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
\SV
m4_lab()
\TLV
/board
/fpga
|led
@0
m4+fpga_refresh($refresh, m4_ifelse(M4_MAKERCHIP, 1, 1, 50000000))
$reset = *reset;
?$refresh
$Leds[15:0] <= $reset ? 1 : $Leds + 1;
*led = $Leds;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000'])
\SV
endmodule
1.2 编译逻辑展现

1.3 动态波形展现

1.4 模拟运行展现

2 七段数码管显示
2.1 RT Verilog编码
0~15 十六进制计数器,在四位七段数码管上动态扫描显示。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
\SV
m4_lab()
\TLV
/board
/fpga
|seven_segment
@0
m4+fpga_heartbeat($refresh, 1, 500000)
$reset = *reset;
?$refresh
//All 4 digits can be enabled by sending logic '0'.
//Each segment can be enabled by sending logic '0'
$digit[3:0] = 4'b0000;
$LedBcd[3:0] <= $reset ? 4'h0 : $LedBcd + 1;
m5+sseg_decoder($LedBcd)
$dp = 1;
*sseg_digit_n = $digit;
*sseg_segment_n = $LedBcd;
*sseg_decimal_point_n = $dp;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000']) // 3rd arg selects the board.
\SV
endmodule
2.2 编译逻辑展现

2.3 动态波形展现

2.4 模拟运行展现

3 LCD16´2显示器
3.1 RT Verilog编码
在 LCD 上显示两行文字,并实现移屏效果。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
\SV
m4_lab()
\TLV
/board
/fpga
|lcd
@0
$reset = *reset;
m4+fpga_refresh($refresh, m4_ifelse(M4_MAKERCHIP, 1, 1, 50000000))
?$refresh // clock gating
$Ii[3:0] <= $reset ? 5'b11111 : ($Ii < 4) ? $Ii + 1 : 0;
$Jj[4:0] <= $reset ? 5'b11111 : ($Ii == 4) ? $Jj + 1 : ($Jj == 24) ? 4 : $Jj;
// data and command instructions
$datas[25*8-1:0] = {8'h18, 8'h2D, 8'h2D, 8'hC0, 8'h21, 8'h21, 8'h21, 8'h6E, 8'h75,
8'h66, 8'h20, 8'h65, 8'h72, 8'h61, 8'h20, 8'h73, 8'h41, 8'h47, 8'h50,
8'h46, 8'h80, 8'h01, 8'h06, 8'h0C, 8'h38};
// select the data
$out[7:0] = $reset ? 0 : ($Ii <= 2) ? $datas >> 8*$Jj : >>1$out;
// set wait time
$lcd_enable = $reset ? 0 : ($Ii <= 2) ? 1 : ($Ii > 2 & $Ii < 4) ? 0 : >>1$lcd_enable;;
// set command/data instruction
$lcd_reset = ($Jj > 4 & $Jj != 21 & $Jj != 24) ? 1 : 0;
*out = $out;
*lcd_enable = $lcd_enable;
*lcd_reset = $lcd_reset;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000']) //3rd arg selects the board.
m4+fpga_lcd()
\SV
endmodule
3.2 编译逻辑展现

3.3 动态波形展现

3.4 模拟运行展现

4 VGA图形显示
4.1 RT Verilog编码
640×480 VGA 输出,绘制一个橙色方块在蓝色背景上。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
\SV
m4_lab()
logic [9:0] sx;
logic [9:0] sy;
m4_ifelse_block(M4_MAKERCHIP, 1,['
m4_define(M4_COUNTER, 32) // required for VIZ
m4_define(M4_FRAMES, 2) // required for VIZ
'],['m4_define(['M4_COUNTER'], 1)'] )
\TLV init_monitor()
m4_define(M4_HA_END, 639)
m4_define(M4_HS_STA, M4_HA_END + 16)
m4_define(M4_HS_END, M4_HS_STA + 96)
m4_define(M4_LINE, 799)
m4_define(M4_VA_END, 479)
m4_define(M4_VS_STA, M4_VA_END + 10)
m4_define(M4_VS_END, M4_VS_STA + 2)
m4_define(M4_SCREEN, 524)
\TLV init_cursor($_reset, $hsync, $vsync, M4_COUNTER)
m4+fpga_refresh($sync, m4_ifelse(M4_MAKERCHIP, 1, 1, 4))
?$sync
$hsync = ~($Sx >= M4_HS_STA && $Sx < M4_HS_END);
$vsync = ~($Sy >= M4_VS_STA && $Sy < M4_VS_END);
$Sx[9:0] <= $_reset ? 10'b0 : $Sx >= M4_LINE ? 0 : $Sx + M4_COUNTER;
$Sy[9:0] <= $_reset ? 10'b0 : ($Sx >= M4_LINE ? ($Sy >= M4_SCREEN ? 0 : $Sy + M4_COUNTER) : $RETAIN);
*sx = $Sx;
*sy = $Sy;
\TLV
/board
/fpga
|vga
@0
m4+init_monitor()
m4+init_cursor($reset, $hsync, $vsync, M4_COUNTER)
m4+fpga_refresh($refresh, m4_ifelse(M4_MAKERCHIP, 1, 1, 4))
$reset = *reset;
?$refresh
$de = (*sx < M4_HA_END && *sy < M4_VA_END);
$qq_draw = (*sx < 64 && *sy < 64);
$vga_hsync = $reset ? 0 : >>1$hsync;
$vga_vsync = $reset ? 0 : >>1$vsync;
$vga_r[3:0] = $reset ? 0 : !$de ? 4'h0 : ($qq_draw ? 4'hF : 4'h0);
$vga_g[3:0] = $reset ? 0 : !$de ? 4'h0 : ($qq_draw ? 4'h8 : 4'h8);
$vga_b[3:0] = $reset ? 0 : !$de ? 4'h0 : ($qq_draw ? 4'h0 : 4'hF);
*vga_hsync = $vga_hsync;
*vga_vsync = $vga_vsync;
*vga_r = $vga_r;
*vga_g = $vga_g;
*vga_b = $vga_b;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000']) // 3rd arg selects the board.
m4+fpga_vga(/board, M4_COUNTER)
\SV
endmodule
4.2 编译逻辑展现

4.3 动态波形展现

4.4 模拟运行展现

5 温度传感器
5.1 RT Verilog编码
模拟温度传感器采集,将读数显示在七段数码管上。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
m4_lab()
function [15:0] sqrt;
input [31:0] x;
reg [15:0] root;
reg [31:0] trial;
integer i;
begin
root = 0;
for (i = 15; i >= 0; i = i - 1) begin
trial = (root | (1 << i));
if (trial * trial <= x)
root = root | (1 << i);
end
sqrt = root;
end
endfunction
\TLV
/board
/fpga
|temp_pipe
@0
$reset = *reset;
m4_ifelse_block(M4_MAKERCHIP, 1,['
$left[5:0] = 0;
$check = (/board/fpga|sseg_pipe$Count == 0);
$top[5:0] = $reset ? 0 : $check ? >>1$top + 1 : $RETAIN;
$value[15:0] = sqrt(((640 - $left) ** 2) + ((640 - $top) ** 2));
$max[7:0] = 150;
$data_adc[15:0] = ($value < 816) ? 16'd20 :
($value > 905) ? 16'd150 :
((($value - 816) * ($max - 20) / 89) + 20);
\viz_alpha
initEach(){
let temp_val = '/board/fpga|temp_pipe$max'.asInt()
let block_square = new fabric.Rect(
{originX: "center", originY: "center",
width: 490, height: 300, fill: "transparent"})
let logic_block = new fabric.Group([block_square],
{originX: "center", originY: "center",
angle: 0, width: 20, height: 20})
let sun_img_url = "https://user-images.githubusercontent.com/64545984/130670851-c7a3b547-7cd0-42ce-9181-52af3a62c35d.png"
let sun_img = new fabric.Image.fromURL(
sun_img_url,
function (img) {
let value = new fabric.Text(temp_val + "C", {
left: -236, top: 130, fontSize: 15,
fill: "black", fontFamily: "Courier New"})
logic_block.add(img)
logic_block.add(value)
global.canvas.bringToFront(logic_block)
global.canvas.renderAll()
},
{originX: "center", originY: "center",
left: -220, top: 140,
scaleX: 0.6, scaleY: 0.6, angle: 0})
return {objects:{logic_block}}
},
renderEach(){
let left = '|temp_pipe$left'.asInt()
let top = '|temp_pipe$top'.asInt()
this.getInitObjects().logic_block.animate(
{left: left * 10, top: -top * 10},
{onChange: this.global.canvas.renderAll.bind(this.global.canvas),
duration: 300})
}
'],['$data_adc[15:0] = 16'd100;'] )
|sseg_pipe
@0
$data_adc[15:0] = /board/fpga|temp_pipe$data_adc;
m4+fpga_heartbeat($refresh, 1, 500000)
$reset = *reset;
?$refresh
$Count[1:0] <= $reset ? 0 : $Count + 1;
$digit[3:0] = $Count == 0 ? 4'b1110 :
$Count == 1 ? 4'b1101 :
$Count == 2 ? 4'b1011 :
4'b0111;
$led_bcd[3:0] = ($Count == 0) ? ((($data_adc % 1000) % 100) % 10) :
($Count == 1) ? (($data_adc % 1000) % 100) / 10 :
($Count == 2) ? ($data_adc % 1000) / 100 :
$data_adc / 1000;
$sseg[6:0] = ($led_bcd == 0) ? 7'b1000000 :
($led_bcd == 1) ? 7'b1001111 :
($led_bcd == 2) ? 7'b0100100 :
($led_bcd == 3) ? 7'b0000110 :
($led_bcd == 4) ? 7'b0001101 :
($led_bcd == 5) ? 7'b0010010 :
($led_bcd == 6) ? 7'b0100000 :
($led_bcd == 7) ? 7'b1001110 :
($led_bcd == 8) ? 7'b0000000 :
($led_bcd == 9) ? 7'b0000100 :
7'b1111111;
$dp = 1;
*sseg_digit_n = $digit;
*sseg_segment_n = $sseg;
*sseg_decimal_point_n = $dp;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000'])
\SV
endmodule
5.2 编译逻辑展现

5.3 动态波形展现

5.4 模拟运行展现

6 光传感器
6.1 RT Verilog编码
如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
m4_lab()
function [15:0] sqrt;
input [31:0] x;
reg [15:0] root;
reg [31:0] trial;
integer i;
begin
root = 0;
for (i = 15; i >= 0; i = i - 1) begin
trial = (root | (1 << i));
if (trial * trial <= x)
root = root | (1 << i);
end
sqrt = root;
end
endfunction
\TLV
/board
/fpga
|ldr_pipe
@0
$reset = *reset;
m4_ifelse_block(M4_MAKERCHIP, 1,['
$left[5:0] = 0;
$check = (/board/fpga|sseg_pipe$Count == 0);
$top[5:0] = $reset ? 0 : $check ? >>1$top + 1 : $RETAIN;
$value[15:0] = sqrt(((640 - $left) ** 2) + ((640 - $top) ** 2));
$max[7:0] = 80;
$data_adc[15:0] = ($value < 816) ? 16'd20 :
($value > 905) ? 16'd80 :
((($value - 816) * ($max - 20) / 89) + 20);
\viz_alpha
initEach(){
let ldr_val = '/board/fpga|ldr_pipe$max'.asInt()
let block_square = new fabric.Rect(
{originX: "center", originY: "center",
width: 490, height: 300, fill: "transparent"})
let logic_block = new fabric.Group([block_square],
{originX: "center", originY: "center",
angle: 0, width: 20, height: 20})
let sun_img_url = "https://user-images.githubusercontent.com/64545984/130670980-8f1723d6-e370-413b-a77b-af068607de30.png"
let sun_img = new fabric.Image.fromURL(
sun_img_url,
function (img) {
let value = new fabric.Text(ldr_val + "C", {
left: -280, top: 100, fontSize: 15,
fill: "darkred", fontFamily: "Courier New"})
logic_block.add(img)
logic_block.add(value)
global.canvas.bringToFront(logic_block)
global.canvas.renderAll()
},
{originX: "center", originY: "center",
left: -270, top: 140,
scaleX: 0.15, scaleY: 0.15, angle: 0})
return {objects:{logic_block}}
},
renderEach(){
let left = '|ldr_pipe$left'.asInt()
let top = '|ldr_pipe$top'.asInt()
this.getInitObjects().logic_block.animate(
{left: left * 10, top: -top * 10},
{onChange: this.global.canvas.renderAll.bind(this.global.canvas),
duration: 300})
}
'],['$data_adc[15:0] = 16'd100;'] )
|sseg_pipe
@0
$data_adc[15:0] = /board/fpga|ldr_pipe$data_adc;
m4+fpga_heartbeat($refresh, 1, 500000)
$reset = *reset;
?$refresh
$Count[1:0] <= $reset ? 0 : $Count + 1;
$digit[3:0] = $Count == 0 ? 4'b1110 :
$Count == 1 ? 4'b1101 :
$Count == 2 ? 4'b1011 :
4'b0111;
$led_bcd[3:0] = ($Count == 0) ? ((($data_adc % 1000) % 100) % 10) :
($Count == 1) ? (($data_adc % 1000) % 100) / 10 :
($Count == 2) ? ($data_adc % 1000) / 100 :
$data_adc / 1000;
$sseg[6:0] = ($led_bcd == 0) ? 7'b1000000 :
($led_bcd == 1) ? 7'b1001111 :
($led_bcd == 2) ? 7'b0100100 :
($led_bcd == 3) ? 7'b0000110 :
($led_bcd == 4) ? 7'b0001101 :
($led_bcd == 5) ? 7'b0010010 :
($led_bcd == 6) ? 7'b0100000 :
($led_bcd == 7) ? 7'b1001110 :
($led_bcd == 8) ? 7'b0000000 :
($led_bcd == 9) ? 7'b0000100 :
7'b1111111;
$dp = 1;
*sseg_digit_n = $digit;
*sseg_segment_n = $sseg;
*sseg_decimal_point_n = $dp;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000'])
\SV
endmodule
6.2 编译逻辑展现

6.3 动态波形展现

6.4 模拟运行展现

7 按钮操控
7.1 RT Verilog编码
按下一个按钮,增加一个LED亮指示,全部松开则LED全灭,循环往复。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
\SV
m4_lab()
\TLV
/board
/fpga
|btn
@0
// 心跳:仿真模式每 1 周期脉冲一次(与官方 seven_segment_counter 相同参数)
m4+fpga_heartbeat($refresh, 1, 500000)
`BOGUS_USE($refresh)
$reset = *reset;
// 在 $refresh 脉冲时递增(<= 在 ?$ 块内 = 已验证的官方模式)
?$refresh
$Count[2:0] <= $reset ? 3'd0 : $Count + 3'd1;
// 从计数器低位提取 3 位索引
$idx[2:0] = $Count;
// 按钮驱动:0~4 对应 5 个按钮,5~7 全松
*push[4:0] = ($idx == 3'd0) ? 5'b00001 :
($idx == 3'd1) ? 5'b00010 :
($idx == 3'd2) ? 5'b00100 :
($idx == 3'd3) ? 5'b01000 :
($idx == 3'd4) ? 5'b10000 :
5'd0;
// LED 累积查表(纯组合,无环路)
*led[15:0] = ($idx == 3'd0) ? 16'b0000000000000001 :
($idx == 3'd1) ? 16'b0000000000000011 :
($idx == 3'd2) ? 16'b0000000000000111 :
($idx == 3'd3) ? 16'b0000000000001111 :
($idx == 3'd4) ? 16'b0000000000011111 :
16'd0;
m4+board(/board, /fpga, 3, *, ['top: 0, left: -1500, width: 7000, height: 7000'])
\SV
endmodule
7.2 编译逻辑展现

7.3 动态波形展现

7.4 模拟运行展现

8 PWM与RGB指示
8.1 RT Verilog编码
根据占空比值在RGB LED中输出不同的颜色,使用了脉宽调制(PWM)的概念。如下文本框所示。
cpp
\m4_TLV_version 1d -p verilog --bestsv --noline: tl-x.org
\SV
m4_include_lib(['https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/tlv_lib/fpga_includes.tlv'])
m4_define(M4_BOARD, 4)
\SV
m4_ifelse_block(M4_MAKERCHIP, 1,['
m4_makerchip_module
logic [4:0] led_r;
logic [4:0] led_g;
logic [4:0] led_b;
'],['
module top(
input wire clk,
input wire reset,
input wire [31:0] cyc_cnt,
output wire passed,
output wire failed,
output [4:0] led_r,
output [4:0] led_g,
output [4:0] led_b
);
'])
\TLV rgb_pwm(|_pipe, @_stage, $led_r, $led_g, $led_b, #dutyr, #dutyg, #dutyb, #led_no)
|_pipe
@_stage
$rst = *reset;
$cnt[7:0] = $rst ? 0 : >>1$cnt + 1;
$led_r = ($cnt < #dutyr);
$led_g = ($cnt < #dutyg);
$led_b = ($cnt < #dutyb);
\viz_alpha
initEach() {
// Diamond layout: top, right, bottom, left, center
// Canvas coords (image offset 0, 0 applied by where viewport)
let positions = [
{left: 220, top: -230},
{left: 240, top: -210},
{left: 220, top: -190},
{left: 200, top: -210},
{left: 220, top: -210}
];
let pos = positions[#led_no];
let led = new fabric.Circle({ left: pos.left, top: pos.top, radius: 8,
fill: "black", opacity: 1, originX: "center", originY: "center" })
return{objects : {led}};
},
renderEach() {
if (typeof global.ledAnimT === "undefined") { global.ledAnimT = 0; }
let t = global.ledAnimT;
global.ledAnimT = (t + 1) % 256;
let phase = #led_no * 51;
let r = Math.round(127.5 + 127.5 * Math.sin((t + phase) * 2 * Math.PI / 256));
let g = Math.round(127.5 + 127.5 * Math.sin((t + phase + 85) * 2 * Math.PI / 256));
let b = Math.round(127.5 + 127.5 * Math.sin((t + phase + 170) * 2 * Math.PI / 256));
let color = "rgb(" + r + "," + g + "," + b + ")";
this.getInitObjects().led.set({fill: color});
}
\TLV
/top
/board_img
\viz_js
init() {
let img = this.newImageFromURL(
"https://raw.githubusercontent.com/os-fpga/Virtual-FPGA-Lab/3760a43f58573fbcf7b7893f13c8fa01da6260fc/viz/icebreaker.png",
"",
{left: 0, top: 0, width: 621, height: 1021},
{angle: 0}
)
return {img: img};
},
renderEach() {
let img = this.getInitObjects().img;
if (img && global.canvas) {
global.canvas.sendToBack(img);
}
},
where: {left: 0, top: 0, width: 621, height: 1021}
m4+rgb_pwm(|led0, @0, $ledr0, $ledg0, $ledb0, 10, 255, 255, 0)
m4+rgb_pwm(|led1, @0, $ledr1, $ledg1, $ledb1, 10, 1, 1, 1)
m4+rgb_pwm(|led2, @0, $ledr2, $ledg2, $ledb2, 10, 128, 128, 2)
m4+rgb_pwm(|led3, @0, $ledr3, $ledg3, $ledb3, 255, 255, 255, 3)
m4+rgb_pwm(|led4, @0, $ledr4, $ledg4, $ledb4, 128, 128, 128, 4)
|led_rgb_pipe
@0
$led_r[4:0] = {/top/board_img|led4>>0$ledr4, /top/board_img|led3>>0$ledr3, /top/board_img|led2>>0$ledr2, /top/board_img|led1>>0$ledr1, /top/board_img|led0>>0$ledr0};
$led_g[4:0] = {/top/board_img|led4>>0$ledg4, /top/board_img|led3>>0$ledg3, /top/board_img|led2>>0$ledg2, /top/board_img|led1>>0$ledg1, /top/board_img|led0>>0$ledg0};
$led_b[4:0] = {/top/board_img|led4>>0$ledb4, /top/board_img|led3>>0$ledb3, /top/board_img|led2>>0$ledb2, /top/board_img|led1>>0$ledb1, /top/board_img|led0>>0$ledb0};
*led_r = $led_r;
*led_g = $led_g;
*led_b = $led_b;
*passed = *cyc_cnt > 400;
*failed = 1'b0;
\SV
endmodule
8.2 编译逻辑展现

8.3 动态波形展现

8.4 模拟运行展现
特别说明:虚拟FPGA实验室仿真库变更,程序设计,没有处理好电路板图片背景作显示,它始终显示在最顶层,为了不影响指示,特将RGB_LDE指示偏移到了图片的右侧。
