mirror of
https://github.com/MikhaelKaa/zx_cartridge.git
synced 2026-03-16 14:37:57 +03:00
263 lines
9.9 KiB
Verilog
263 lines
9.9 KiB
Verilog
`timescale 1ns / 1ps
|
||
|
||
module tb_zx_cartridge();
|
||
// Управляющие сигналы
|
||
reg reset_n;
|
||
reg iorq_n;
|
||
reg rd_n;
|
||
reg wr_n;
|
||
reg mreq_n;
|
||
|
||
// Полная адресная шина (16 бит)
|
||
reg [15:0] address;
|
||
|
||
// Подключение отдельных бит к DUT
|
||
wire A7 = address[7];
|
||
wire A13 = address[13];
|
||
wire A14 = address[14];
|
||
wire A15 = address[15];
|
||
|
||
// Шина данных (8 бит) – двунаправленная
|
||
wire [7:0] D;
|
||
reg [7:0] D_drive; // данные для записи от тестбенча
|
||
wire [7:0] D_sample; // данные, читаемые из DUT
|
||
assign D = (wr_n == 0) ? D_drive : 8'bz;
|
||
assign D_sample = D;
|
||
|
||
// Выходы DUT
|
||
wire ZX_ROM_blk;
|
||
wire CR_ROM_oe_n;
|
||
wire [5:0] CR_ROM_A;
|
||
wire [3:0] CR_ROM_CS; // теперь 4 бита
|
||
|
||
// Вспомогательная переменная для чтения портов
|
||
reg [7:0] dummy;
|
||
|
||
// Тестируемый модуль (новая версия)
|
||
zx_cartridge uut (
|
||
.reset_n(reset_n),
|
||
.iorq_n(iorq_n),
|
||
.rd_n(rd_n),
|
||
.wr_n(wr_n),
|
||
.mreq_n(mreq_n),
|
||
.A7(A7),
|
||
.A13(A13),
|
||
.A14(A14),
|
||
.A15(A15),
|
||
.D(D),
|
||
.ZX_ROM_blk(ZX_ROM_blk),
|
||
.CR_ROM_oe_n(CR_ROM_oe_n),
|
||
.CR_ROM_A(CR_ROM_A),
|
||
.CR_ROM_CS(CR_ROM_CS)
|
||
);
|
||
|
||
// Задачи для моделирования циклов Z80
|
||
|
||
// Запись в порт ввода-вывода
|
||
task write_port(input [15:0] addr, input [7:0] data);
|
||
begin
|
||
address = addr;
|
||
D_drive = data;
|
||
#10;
|
||
iorq_n = 0;
|
||
wr_n = 0;
|
||
#20;
|
||
iorq_n = 1;
|
||
wr_n = 1;
|
||
#10;
|
||
D_drive = 8'bz;
|
||
end
|
||
endtask
|
||
|
||
// Чтение из порта ввода-вывода (возвращает прочитанные данные)
|
||
task read_port(input [15:0] addr, output [7:0] data);
|
||
begin
|
||
address = addr;
|
||
#10;
|
||
iorq_n = 0;
|
||
rd_n = 0;
|
||
#20;
|
||
data = D_sample;
|
||
iorq_n = 1;
|
||
rd_n = 1;
|
||
#10;
|
||
end
|
||
endtask
|
||
|
||
// Чтение из памяти с проверкой CR_ROM_A и CR_ROM_CS (новые сигналы)
|
||
task read_mem_check(input [15:0] addr, input [5:0] exp_A, input [3:0] exp_CS);
|
||
begin
|
||
address = addr;
|
||
#10;
|
||
mreq_n = 0;
|
||
rd_n = 0;
|
||
#10; // ждём стабилизации
|
||
check_equal(exp_A, CR_ROM_A, "CR_ROM_A during read");
|
||
check_equal(exp_CS, CR_ROM_CS, "CR_ROM_CS during read");
|
||
#10;
|
||
mreq_n = 1;
|
||
rd_n = 1;
|
||
#10;
|
||
end
|
||
endtask
|
||
|
||
// Чтение из памяти с проверкой CR_ROM_oe_n и ZX_ROM_blk
|
||
task read_mem_check_oe(input [15:0] addr, input exp_oe, input exp_blk);
|
||
begin
|
||
address = addr;
|
||
#10;
|
||
mreq_n = 0;
|
||
rd_n = 0;
|
||
#10;
|
||
check_equal(exp_oe, CR_ROM_oe_n, "CR_ROM_oe_n during read");
|
||
check_equal(exp_blk, ZX_ROM_blk, "ZX_ROM_blk during read");
|
||
#10;
|
||
mreq_n = 1;
|
||
rd_n = 1;
|
||
#10;
|
||
end
|
||
endtask
|
||
|
||
// Простое чтение из памяти (без проверки, для установки адреса)
|
||
task read_mem(input [15:0] addr);
|
||
begin
|
||
address = addr;
|
||
#10;
|
||
mreq_n = 0;
|
||
rd_n = 0;
|
||
#20;
|
||
mreq_n = 1;
|
||
rd_n = 1;
|
||
#10;
|
||
end
|
||
endtask
|
||
|
||
// Проверка равенства (поддерживает 4‑битные и 6‑битные аргументы)
|
||
task check_equal(input [31:0] expected, input [31:0] actual, input [80*8:0] msg);
|
||
if (expected !== actual) begin
|
||
$display("ERROR: %s. Expected %h, got %h", msg, expected, actual);
|
||
end else begin
|
||
$display("OK: %s", msg);
|
||
end
|
||
endtask
|
||
|
||
initial begin
|
||
$dumpfile("zx_cartridge.vcd");
|
||
$dumpvars(0, tb_zx_cartridge);
|
||
|
||
// Исходное состояние: сброс активен, все сигналы неактивны
|
||
reset_n = 0;
|
||
iorq_n = 1;
|
||
rd_n = 1;
|
||
wr_n = 1;
|
||
mreq_n = 1;
|
||
address = 16'h0000;
|
||
D_drive = 8'bz;
|
||
#100;
|
||
reset_n = 1;
|
||
#10;
|
||
|
||
// ------------------------------------------------------------
|
||
// Test 1: Запись и чтение регистров через порты
|
||
// ------------------------------------------------------------
|
||
$display("=== Test 1: Write and read registers via I/O ports ===");
|
||
|
||
// Запись в bank: адрес 0xC000 (A15=1, A14=1, A13=0, A7=0)
|
||
write_port(16'hC000, 8'hA5); // запись reg_bank = 0xA5
|
||
read_port(16'hC000, dummy);
|
||
check_equal(8'hA5, dummy, "Read bank returns written value");
|
||
|
||
// Запись в control: адрес 0xA000 (A15=1, A14=0, A13=1, A7=0)
|
||
write_port(16'hA000, 8'h80); // запись reg_ctl с битом 7 = 1 (отключение)
|
||
read_port(16'hA000, dummy);
|
||
check_equal(8'h80, dummy, "Read control returns written value");
|
||
|
||
// Сбрасываем бит disable (reg_ctl[7]=0) для дальнейших тестов
|
||
write_port(16'hA000, 8'h00);
|
||
read_port(16'hA000, dummy);
|
||
check_equal(8'h00, dummy, "Control = 0 after disable cleared");
|
||
|
||
// ------------------------------------------------------------
|
||
// Test 2: Формирование страницы и выбор микросхемы
|
||
// ------------------------------------------------------------
|
||
$display("=== Test 2: Page and chip select formation ===");
|
||
|
||
// Записываем bank = 0xA5 -> chip_sel = 2'b10 = 2, page_offs = 6'b100101 = 37
|
||
write_port(16'hC000, 8'hA5);
|
||
|
||
// Верхнее окно (A13=1): адрес 0x2000
|
||
// Ожидаем CR_ROM_A = 37, активный CS2 (бит 2 = 0) -> 4'b1011 (младший бит = CS0)
|
||
read_mem_check(16'h2000, 6'd37, 4'b1011); // CS2 активен (0), остальные 1
|
||
|
||
// Нижнее окно (A13=0): адрес 0x1000
|
||
// Ожидаем CR_ROM_A = 0, активный CS0 -> 4'b1110
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||
|
||
// ------------------------------------------------------------
|
||
// Test 3: Проверка всех вариантов chip_sel
|
||
// ------------------------------------------------------------
|
||
$display("=== Test 3: Chip select generation for all chip_sel values ===");
|
||
|
||
// chip_sel = 0
|
||
write_port(16'hC000, 8'h00); // 0b00000000
|
||
read_mem_check(16'h2000, 6'd0, 4'b1110); // CS0 активен (0) -> 1110
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно тоже CS0
|
||
|
||
// chip_sel = 1
|
||
write_port(16'hC000, 8'h40); // 0b01000000 -> chip_sel=1, offs=0
|
||
read_mem_check(16'h2000, 6'd0, 4'b1101); // CS1 активен -> 1101
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно CS0
|
||
|
||
// chip_sel = 2
|
||
write_port(16'hC000, 8'h80); // 0b10000000 -> chip_sel=2, offs=0
|
||
read_mem_check(16'h2000, 6'd0, 4'b1011); // CS2 активен -> 1011
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||
|
||
// chip_sel = 3
|
||
write_port(16'hC000, 8'hC0); // 0b11000000 -> chip_sel=3, offs=0
|
||
read_mem_check(16'h2000, 6'd0, 4'b0111); // CS3 активен -> 0111
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||
|
||
// ------------------------------------------------------------
|
||
// Test 4: Сигнал rom_access и CR_ROM_oe_n / ZX_ROM_blk
|
||
// ------------------------------------------------------------
|
||
$display("=== Test 4: rom_access control ===");
|
||
|
||
// Включим картридж (reg_ctl[7]=0) – уже 0
|
||
// Чтение из области ROM (адрес 0x1000)
|
||
read_mem_check_oe(16'h1000, 1'b0, 1'b1); // CR_ROM_oe_n = 0, ZX_ROM_blk = 1
|
||
|
||
// Чтение из области не ROM (адрес 0x4000, A15=0, A14=1)
|
||
read_mem_check_oe(16'h4000, 1'b1, 1'b0); // оба неактивны
|
||
|
||
// Отключим картридж (установим бит 7)
|
||
write_port(16'hA000, 8'h80);
|
||
read_mem_check_oe(16'h1000, 1'b1, 1'b0); // неактивны, т.к. картридж отключён
|
||
|
||
// Снова включим
|
||
write_port(16'hA000, 8'h00);
|
||
|
||
// ------------------------------------------------------------
|
||
// Test 5: Сброс
|
||
// ------------------------------------------------------------
|
||
$display("=== Test 5: Reset ===");
|
||
reset_n = 0;
|
||
#20;
|
||
reset_n = 1;
|
||
#10;
|
||
|
||
// Проверим, что регистры сброшены в 0
|
||
read_port(16'hC000, dummy);
|
||
check_equal(8'h00, dummy, "bank reads 0 after reset");
|
||
read_port(16'hA000, dummy);
|
||
check_equal(8'h00, dummy, "control reads 0 after reset");
|
||
|
||
// Проверим поведение после сброса: нижнее окно CS0, страница 0
|
||
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно: CS0 активен
|
||
read_mem_check(16'h2000, 6'd0, 4'b1110); // верхнее окно тоже должно быть CS0 (т.к. bank=0)
|
||
|
||
$display("=== All tests completed ===");
|
||
$finish;
|
||
end
|
||
|
||
endmodule |