例如,我想为一个交互式游戏运行一个模拟,比如:没有FPGA的https://github.com/fabioperez/space-invaders-vhdl,这样:
http://www.nand2tetris.org/是这样做的,但它使用的是一种简化的自定义教育语言。
VHDL的textio
的read(input
和write(output
有点接近,但并不完全是:
read(input
等待换行符,我们希望能检测到的东西是键盘键按或不按write(output
:需要某种方法来刷新数据,以确保将模拟显示的渲染器得到它。当然,我不需要用VHDL做任何事情:我只需要一种最低限度的方式与其他程序同步地与VHDL进行通信,然后我就可以用SDL在C中进行显示。
发布于 2016-07-03 21:52:20
Verilator
Verilator是此应用程序的完美解决方案。
它将Verilog仿真循环公开给C++ (并将Verilog转换为C++),允许您设置输入并从C++获取输出。
参见从docs:http://www.veripool.org/projects/verilator/wiki/Manual-verilator连接到http://www.veripool.org/projects/verilator/wiki/Manual-verilator的示例
因此,您可以直接将其插入SDL / ncurses /等等,而无需任何IPC。
对于独立于模拟器的解决方案,可能值得研究一下VHDL (VHPI) / Verilog (DPI)的外国语API(如前面提到的在本评论中 ),但关于如何使用这些API的例子寥寥无几,您将不得不担心IPC。
最小可运行示例:
在Verilator + SDL中实现nand2tetris的相关项目可以在:https://hackaday.io/project/160865-nand2tetris-in-verilog-part3-verilator-and-sdl2中找到
安装Ubuntu22.04上的依赖项:
sudo apt install libsdl2-dev verilator
Makefile
.POSIX:
.PHONY: all clean run
RUN ?= move
OUT_EXT ?= .out
VERILATOR_DIR = ./obj_dir/
all: $(VERILATOR_DIR)Vmove display$(OUT_EXT)
$(VERILATOR_DIR)Vmove: move.v move.cpp fps.hpp
verilator -Wall --cc move.v --exe move.cpp
make -C obj_dir -f Vmove.mk Vmove CXXFLAGS='--std=c++11 -Wall' LIBS='-lSDL2'
display$(OUT_EXT): display.cpp
g++ -o '$@' '$<' -lm -lSDL2
clean:
rm -rf obj_dir *'$(OUT_EXT)'
run: all
'$(VERILATOR_DIR)V$(RUN)'
move.v
module move(
input wire clock,
input wire reset,
input wire up,
input wire down,
input wire left,
input wire right,
output reg [1:0] x,
output reg [1:0] y
);
always @ (posedge clock) begin
if (reset == 1'b1) begin
x <= 0;
y <= 0;
end
else begin
if (up == 1'b1) begin
y <= y - 1;
end
if (down == 1'b1) begin
y <= y + 1;
end
if (left == 1'b1) begin
x <= x - 1;
end
if (right == 1'b1) begin
x <= x + 1;
end
end
end
endmodule
move.cpp
const char *help = "asdw: move | q: quit";
#include <cmath>
#include <cstdlib>
#include <time.h>
#include <SDL2/SDL.h>
#include "Vmove.h"
#include "verilated.h"
#include "fps.hpp"
#define WINDOW_WIDTH 512
#define RECTS_PER_WINDOW (4)
#define RECT_WIDTH (WINDOW_WIDTH / RECTS_PER_WINDOW)
#define FASTEST_TICK_PERIOD_S (1.0 / 4.0)
int main(int argc, char **argv) {
SDL_Event event;
SDL_Renderer *renderer;
SDL_Window *window;
double current_time_s, last_tick_time_s;
unsigned int current_time, last_time;
const Uint8 *keystate;
Verilated::commandArgs(argc, argv);
Vmove *top = new Vmove;
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window, &renderer);
SDL_SetWindowTitle(window, help);
fps_init();
top->clock = 0;
top->eval();
top->reset = 1;
top->clock = 1;
top->eval();
while (1) {
current_time = SDL_GetTicks();
current_time_s = current_time / 1000.0;
/* Deal with keyboard input. */
while (SDL_PollEvent(&event) == 1) {
if (event.type == SDL_QUIT) {
goto quit;
} else if (event.type == SDL_KEYDOWN) {
switch(event.key.keysym.sym) {
case SDLK_q:
goto quit;
default:
break;
}
}
}
keystate = SDL_GetKeyboardState(NULL);
if (keystate[SDL_SCANCODE_ESCAPE]) {
top->reset = 1;
}
if (keystate[SDL_SCANCODE_A]) {
top->left = 1;
}
if (keystate[SDL_SCANCODE_D]) {
top->right = 1;
}
if (keystate[SDL_SCANCODE_W]) {
top->up = 1;
}
if (keystate[SDL_SCANCODE_S]) {
top->down = 1;
}
if (current_time != last_time) {
if (current_time_s - last_tick_time_s > FASTEST_TICK_PERIOD_S) {
/* Draw world. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
{
SDL_Rect rect;
rect.w = RECT_WIDTH;
rect.h = RECT_WIDTH;
rect.x = top->x * RECT_WIDTH;
rect.y = top->y * RECT_WIDTH;
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderFillRect(renderer, &rect);
}
SDL_RenderPresent(renderer);
top->clock = 0;
top->eval();
top->clock = 1;
top->eval();
top->up = 0;
top->down = 0;
top->left = 0;
top->right = 0;
top->reset = 0;
/* Update time tracking. */
last_tick_time_s = current_time_s;
fps_update_and_print();
}
}
last_time = current_time;
}
quit:
top->final();
delete top;
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
display.cpp
/*
Test a simple virtual SDL display, without user input.
*/
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <SDL2/SDL.h>
#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT (WINDOW_WIDTH)
#define N_PIXELS_WIDTH 10
#define N_PIXELS_HEIGHT (N_PIXELS_WIDTH)
#define N_PIXELS (N_PIXELS_WIDTH * N_PIXELS_HEIGHT)
#define PIXEL_WIDTH (WINDOW_WIDTH / N_PIXELS_WIDTH)
#define PIXEL_HEIGHT (WINDOW_HEIGHT / N_PIXELS_HEIGHT)
#define MAX_COLOR 255
#define PI2 (2*(acos(-1.0)))
#define FREQ (0.05)
int main(int argc, char **argv, char **env) {
SDL_Event event;
SDL_Rect rect;
SDL_Renderer *renderer;
SDL_Window *window;
const unsigned int max_color_half = MAX_COLOR / 2;
int quit;
double current_time_s;
size_t cur, i , j;
unsigned int
bs[N_PIXELS],
current_time,
gs[N_PIXELS],
last_time,
rs[N_PIXELS],
val
;
quit = 0;
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window, &renderer);
rect.w = PIXEL_WIDTH;
rect.h = PIXEL_HEIGHT;
last_time = SDL_GetTicks();
while (!quit) {
while (SDL_PollEvent(&event) == 1) {
if (event.type == SDL_QUIT) {
quit = 1;
}
}
current_time = SDL_GetTicks();
if (current_time != last_time) {
for (i = 0; i < N_PIXELS_WIDTH; ++i) {
for (j = 0; j < N_PIXELS_WIDTH; ++j) {
cur = j * N_PIXELS_WIDTH + i;
val = (1 + i) * (1 + j) * PI2 * FREQ * current_time / 1000.0;
rs[cur] = max_color_half * (1.0 + std::sin(1 * val));
gs[cur] = max_color_half * (1.0 + std::sin(2 * val));
bs[cur] = max_color_half * (1.0 + std::sin(3 * val));
}
}
}
for (i = 0; i < N_PIXELS_WIDTH; ++i) {
for (j = 0; j < N_PIXELS_WIDTH; ++j) {
cur = j *N_PIXELS_WIDTH + i;
SDL_SetRenderDrawColor(renderer, rs[cur], gs[cur], bs[cur], 255);
rect.x = i * PIXEL_WIDTH;
rect.y = j * PIXEL_HEIGHT;
SDL_RenderFillRect(renderer, &rect);
}
}
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
发布于 2016-07-01 15:31:55
康纳塔尔将运行在实际CPU上的软件连接到FPGA或模拟器上的RTL (可链接到VHDL和Verilog)。BSV对于学术和研究用途以及开放源码项目都是免费的。无论如何,Connectal是开源的,软件到模拟器连接使用SystemVerilog DPI,您可以在项目中使用它而不需要使用BSV。
Connectal有一个示例在显示器上显示来自FGPA/模拟器的输出。仿真时使用Qt在计算机显示器上显示。从FPGA,它直接显示在HDMI显示器上。
在Verilog或VHDL中模拟的CPU往往速度太慢,无法交互使用,但我已经将用qemu模拟的CPU连接到了verilator或FPGA上的设备或加速器上。qemu性能良好。我认为这对你的目的是有用的。
我添加了一个插件FgpaOps API,以便模拟器或FPGA能够处理CPU负载/存储指令:
struct FpgaOps {
uint64_t (*read)(hwaddr addr);
void (*write)(hwaddr addr, uint64_t value);
void (*close)(void);
void *(*alloc_mem)(size_t size);
};
在我的例子中,我使用connectal来实现FpgaOps
插件。此代码位于hw/riscv下,但不特定于riscv,因此它可以与qemu支持的任何处理器体系结构一起使用。
发布于 2021-05-17 01:16:42
不需要任何太聪明或定制的东西来与您的Sim交互,只要您愿意模拟像UART这样的真正的硬件接口。
我的TTL的Verilog模拟包括一个(合理)精确的UM245R UART模型,UART通过verilog文件接口支持交互式IO。双向的用于输入和输出的文件;双向的。
我用它与模拟的硬件进行交互,这样我就可以开发软件并通过自动测试测试它,而不必与硬件混淆。我甚至在模拟硬件上运行CHIP8游戏,CHIP8图形用户界面是通过将控制代码从UART发送回图形终端来绘制的。
UART在这里. https://github.com/Johnlon/spam-1/blob/master/verilog/uart/um245r.v
总有一天我会把它写下来。
https://stackoverflow.com/questions/38108243
复制相似问题