Its work!

This commit is contained in:
MichailKaa 2026-02-17 23:05:03 +03:00
parent 2f6216192d
commit a69392707f
3 changed files with 120 additions and 66 deletions

View File

@ -4,7 +4,7 @@
// CPU adr bus A0...A12 connect directly to CR_ROM chip
module zx_cartrige #(
// default example parameter
parameter SELF_LOCK_VAL = 10
parameter SELF_LOCK_VAL = 15
)(
// Reset
input reset_n,

View File

@ -1,17 +1,27 @@
`timescale 1ns / 1ps
module tb_zx_cartrige();
// Управляющие сигналы
reg reset_n;
reg iorq_n;
reg rd_n;
reg mreq_n;
reg A7, A13, A14, A15;
// Полная адресная шина (16 бит)
reg [15:0] address;
// Подключение отдельных бит к DUT
wire A7 = address[7];
wire A13 = address[13];
wire A14 = address[14];
wire A15 = address[15];
// Выходы DUT
wire ZX_ROM_blk;
wire CR_ROM_oe_n;
wire [5:0] CR_ROM_A;
// DUT с уменьшенным параметром для быстрой проверки
// Тестируемый модуль (с уменьшенным параметром для быстрой проверки)
zx_cartrige #(
.SELF_LOCK_VAL(3)
) uut (
@ -28,19 +38,50 @@ module tb_zx_cartrige();
.CR_ROM_A(CR_ROM_A)
);
// Задачи для моделирования циклов Z80
// Запись в порт (активируется iorq_n, для инкремента важен его спад)
task write_port(input [15:0] addr);
begin
address = addr;
#10;
iorq_n = 0; // начало цикла IN/OUT
#10;
iorq_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
// Проверка с выводом сообщения
task check_equal(input [31:0] expected, input [31:0] actual, input [80*8:0] msg);
if (expected !== actual) begin
$display("ERROR: %s. Expected %d, got %d", msg, expected, actual);
end
endtask
initial begin
$dumpfile("tb_zx_cartrige.vcd");
$dumpfile("zx_cartrige.vcd");
$dumpvars(0, tb_zx_cartrige);
// Исходное состояние: сброс активен
// Исходное состояние: сброс активен, все сигналы неактивны
reset_n = 0;
iorq_n = 1;
rd_n = 1;
mreq_n = 1;
A7 = 0;
A13 = 0;
A14 = 0;
A15 = 0;
address = 16'h0000;
#100;
reset_n = 1;
#10;
@ -49,90 +90,103 @@ module tb_zx_cartrige();
// Test 1: Инкремент происходит только при A7=0 и спаде iorq_n
// ------------------------------------------------------------
$display("=== Test 1: Increment condition (A7=0 and iorq_n falling) ===");
check_equal(0, CR_ROM_A, "Initial CR_ROM_A");
if (CR_ROM_A !== 0) $display("ERROR: Initial CR_ROM_A = %d, expected 0", CR_ROM_A);
// Попытка с A7=1 не должен инкрементироваться
write_port(16'h0080); // A7=1 (адрес 0x80)
#10;
check_equal(0, CR_ROM_A, "After write to port 0x80 (A7=1)");
// Попытка инкремента с A7=1 не должен инкрементироваться
A7 = 1;
iorq_n = 0; // rom_page_up = 0|1|0 = 1 нет изменения
// Корректный инкремент с A7=0
write_port(16'h007F); // A7=0
#10;
iorq_n = 1;
#10;
if (CR_ROM_A !== 0) $display("ERROR: Increment occurred while A7=1, CR_ROM_A = %d", CR_ROM_A);
check_equal(1, CR_ROM_A, "After first write to 0x7F");
// Теперь A7=0, создаём отрицательный фронт iorq_n
A7 = 0;
iorq_n = 1;
#10;
iorq_n = 0; // rom_page_up: 1->0 -> negedge
#10;
if (CR_ROM_A !== 1) $display("ERROR: No increment when A7=0 and iorq_n falling, CR_ROM_A = %d", CR_ROM_A);
iorq_n = 1;
write_port(16'h007F); // второй раз
#10;
check_equal(2, CR_ROM_A, "After second write to 0x7F");
// ------------------------------------------------------------
// Test 2: Достижение SELF_LOCK_VAL блокирует активацию CR_ROM_oe_n
// Test 2: Достижение SELF_LOCK_VAL (3) блокирует дальнейшие инкременты
// ------------------------------------------------------------
$display("=== Test 2: Self-lock prevents CR_ROM_oe_n activation ===");
// Инкрементируем до 2
iorq_n = 0; #10; iorq_n = 1; #10; // CR_ROM_A=2
iorq_n = 0; #10; iorq_n = 1; #10; // CR_ROM_A=3 (lock)
if (CR_ROM_A !== 3) $display("ERROR: Failed to reach lock, CR_ROM_A = %d", CR_ROM_A);
// self_lock активен. Проверим, что CR_ROM_oe_n всегда 1 при попытке активации
// Необходимые условия: lower_rom=1 (A13=A14=A15=0), rd_n=0, mreq_n=0
A13 = 0; A14 = 0; A15 = 0;
rd_n = 0;
mreq_n = 0;
$display("=== Test 2: Self-lock at value 3 ===");
write_port(16'h007F); // третий раз -> lock
#10;
if (CR_ROM_oe_n !== 0) $display("ERROR: CR_ROM_oe_n = %b, expected 1 (self_lock active)", CR_ROM_oe_n);
if (ZX_ROM_blk !== 1) $display("ERROR: ZX_ROM_blk = %b, expected 0", ZX_ROM_blk);
check_equal(3, CR_ROM_A, "After third write (should lock)");
rd_n = 1; mreq_n = 1;
// Попытка инкремента после блокировки
write_port(16'h007F);
#10;
check_equal(3, CR_ROM_A, "Write after lock - no increment");
// ------------------------------------------------------------
// Test 3: CR_ROM_oe_n активируется при обращении в нижние б
// Условия: lower_rom=1, rd_n=0, mreq_n=0, self_lock=0
// Test 3: При self_lock=1 CR_ROM_oe_n не активируется даже в нижней ROM
// ------------------------------------------------------------
$display("=== Test 3: CR_ROM_oe_n activation in lower ROM area ===");
$display("=== Test 3: CR_ROM_oe_n inactive while locked ===");
read_mem(16'h0100); // адрес в нижней области (0x100)
#10;
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n during locked read");
check_equal(0, ZX_ROM_blk, "ZX_ROM_blk during locked read");
// Сброс для снятия self_lock
// ------------------------------------------------------------
// Test 4: Сброс обнуляет счётчик и снимает блокировку
// ------------------------------------------------------------
$display("=== Test 4: Reset ===");
reset_n = 0;
#10;
#20;
reset_n = 1;
#10;
if (CR_ROM_A !== 0) $display("ERROR: After reset CR_ROM_A = %d, expected 0", CR_ROM_A);
check_equal(0, CR_ROM_A, "After reset");
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n after reset");
// Устанавливаем условия для активации
A13 = 0; A14 = 0; A15 = 0; // lower_rom = 1
rd_n = 0;
mreq_n = 0;
// ------------------------------------------------------------
// Test 5: Активация CR_ROM_oe_n при чтении нижних 8KB (self_lock=0)
// ------------------------------------------------------------
$display("=== Test 5: CR_ROM_oe_n activation in lower ROM (0x0000-0x1FFF) ===");
// Чтение внутри нижней области
read_mem(16'h0100);
#10;
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x100");
check_equal(0, ZX_ROM_blk, "ZX_ROM_blk at 0x100");
read_mem(16'h1FFF); // граница нижней области
#10;
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x1FFF");
// Чтение вне нижней области
read_mem(16'h2000); // A13=1
#10;
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x2000 (outside)");
read_mem(16'h4001); // A14=1
#10;
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x4001 (outside)");
// ------------------------------------------------------------
// Test 6: Проверка влияния mreq_n и rd_n
// ------------------------------------------------------------
$display("=== Test 6: Control signals mreq_n and rd_n ===");
address = 16'h0100;
#10;
// Ожидаем CR_ROM_oe_n = 0 (активен)
if (CR_ROM_oe_n !== 0) $display("ERROR: CR_ROM_oe_n = %b, expected 0 during lower ROM access (rd_n=0, mreq_n=0)", CR_ROM_oe_n);
if (ZX_ROM_blk !== 1) $display("ERROR: ZX_ROM_blk = %b, expected 1", ZX_ROM_blk);
// Проверка, что при выходе из нижней области (lower_rom=0) выход отключается
A13 = 1; // теперь lower_rom = 0 (A13=1, остальные 0)
// mreq_n=0, rd_n=1 чтение не активно
mreq_n = 0; rd_n = 1;
#10;
if (CR_ROM_oe_n !== 1) $display("ERROR: CR_ROM_oe_n = %b, expected 1 when not in lower ROM", CR_ROM_oe_n);
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n with rd_n=1");
// Проверка, что при rd_n=1 выход отключается
A13 = 0; // обратно в lower_rom=1
rd_n = 1;
// mreq_n=1, rd_n=0 нет запроса памяти
mreq_n = 1; rd_n = 0;
#10;
if (CR_ROM_oe_n !== 1) $display("ERROR: CR_ROM_oe_n = %b, expected 1 when rd_n=1", CR_ROM_oe_n);
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n with mreq_n=1");
// Проверка, что при mreq_n=1 выход отключается
rd_n = 0; mreq_n = 1;
// Оба активны должно включиться
mreq_n = 0; rd_n = 0;
#10;
if (CR_ROM_oe_n !== 1) $display("ERROR: CR_ROM_oe_n = %b, expected 1 when mreq_n=1", CR_ROM_oe_n);
check_equal(0, CR_ROM_oe_n, "CR_ROM_oe_n with both active");
rd_n = 1; mreq_n = 1;
// Возврат в исходное
mreq_n = 1; rd_n = 1;
#10;
$display("=== All tests completed ===");

Binary file not shown.