首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否可以用VHDL或Verilog进行交互式用户输入和输出仿真?

是否可以用VHDL或Verilog进行交互式用户输入和输出仿真?
EN

Stack Overflow用户
提问于 2016-06-29 19:13:38
回答 3查看 6.1K关注 0票数 1

例如,我想为一个交互式游戏运行一个模拟,比如:没有FPGA的https://github.com/fabioperez/space-invaders-vhdl,这样:

  • 信号由键盘键设置。
  • 输出可以显示在窗口上。

http://www.nand2tetris.org/是这样做的,但它使用的是一种简化的自定义教育语言。

VHDL的textioread(inputwrite(output有点接近,但并不完全是:

  • read(input等待换行符,我们希望能检测到的东西是键盘键按或不按
  • write(output:需要某种方法来刷新数据,以确保将模拟显示的渲染器得到它。
  • 我们需要一些方法来控制模拟速度。

当然,我不需要用VHDL做任何事情:我只需要一种最低限度的方式与其他程序同步地与VHDL进行通信,然后我就可以用SDL在C中进行显示。

还询问:https://github.com/tgingold/ghdl/issues/92

EN

回答 3

Stack Overflow用户

发布于 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上的依赖项:

代码语言:javascript
运行
复制
sudo apt install libsdl2-dev verilator

Makefile

代码语言:javascript
运行
复制
.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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
/*
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;
}

GitHub上游

票数 7
EN

Stack Overflow用户

发布于 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负载/存储指令:

代码语言:javascript
运行
复制
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支持的任何处理器体系结构一起使用。

票数 3
EN

Stack Overflow用户

发布于 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

总有一天我会把它写下来。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38108243

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档