mirror of
https://github.com/MikhaelKaa/zx_cartridge.git
synced 2026-03-16 22:47:56 +03:00
Its work!
This commit is contained in:
parent
2f6216192d
commit
a69392707f
@ -4,7 +4,7 @@
|
|||||||
// CPU adr bus A0...A12 connect directly to CR_ROM chip
|
// CPU adr bus A0...A12 connect directly to CR_ROM chip
|
||||||
module zx_cartrige #(
|
module zx_cartrige #(
|
||||||
// default example parameter
|
// default example parameter
|
||||||
parameter SELF_LOCK_VAL = 10
|
parameter SELF_LOCK_VAL = 15
|
||||||
)(
|
)(
|
||||||
// Reset
|
// Reset
|
||||||
input reset_n,
|
input reset_n,
|
||||||
|
|||||||
@ -1,17 +1,27 @@
|
|||||||
`timescale 1ns / 1ps
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
module tb_zx_cartrige();
|
module tb_zx_cartrige();
|
||||||
|
// Управляющие сигналы
|
||||||
reg reset_n;
|
reg reset_n;
|
||||||
reg iorq_n;
|
reg iorq_n;
|
||||||
reg rd_n;
|
reg rd_n;
|
||||||
reg mreq_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 ZX_ROM_blk;
|
||||||
wire CR_ROM_oe_n;
|
wire CR_ROM_oe_n;
|
||||||
wire [5:0] CR_ROM_A;
|
wire [5:0] CR_ROM_A;
|
||||||
|
|
||||||
// DUT с уменьшенным параметром для быстрой проверки
|
// Тестируемый модуль (с уменьшенным параметром для быстрой проверки)
|
||||||
zx_cartrige #(
|
zx_cartrige #(
|
||||||
.SELF_LOCK_VAL(3)
|
.SELF_LOCK_VAL(3)
|
||||||
) uut (
|
) uut (
|
||||||
@ -28,19 +38,50 @@ module tb_zx_cartrige();
|
|||||||
.CR_ROM_A(CR_ROM_A)
|
.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
|
initial begin
|
||||||
$dumpfile("tb_zx_cartrige.vcd");
|
$dumpfile("zx_cartrige.vcd");
|
||||||
$dumpvars(0, tb_zx_cartrige);
|
$dumpvars(0, tb_zx_cartrige);
|
||||||
|
|
||||||
// Исходное состояние: сброс активен
|
// Исходное состояние: сброс активен, все сигналы неактивны
|
||||||
reset_n = 0;
|
reset_n = 0;
|
||||||
iorq_n = 1;
|
iorq_n = 1;
|
||||||
rd_n = 1;
|
rd_n = 1;
|
||||||
mreq_n = 1;
|
mreq_n = 1;
|
||||||
A7 = 0;
|
address = 16'h0000;
|
||||||
A13 = 0;
|
|
||||||
A14 = 0;
|
|
||||||
A15 = 0;
|
|
||||||
#100;
|
#100;
|
||||||
reset_n = 1;
|
reset_n = 1;
|
||||||
#10;
|
#10;
|
||||||
@ -49,90 +90,103 @@ module tb_zx_cartrige();
|
|||||||
// Test 1: Инкремент происходит только при A7=0 и спаде iorq_n
|
// Test 1: Инкремент происходит только при A7=0 и спаде iorq_n
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
$display("=== Test 1: Increment condition (A7=0 and iorq_n falling) ===");
|
$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=0
|
||||||
A7 = 1;
|
write_port(16'h007F); // A7=0
|
||||||
iorq_n = 0; // rom_page_up = 0|1|0 = 1 – нет изменения
|
|
||||||
#10;
|
#10;
|
||||||
iorq_n = 1;
|
check_equal(1, CR_ROM_A, "After first write to 0x7F");
|
||||||
#10;
|
|
||||||
if (CR_ROM_A !== 0) $display("ERROR: Increment occurred while A7=1, CR_ROM_A = %d", CR_ROM_A);
|
|
||||||
|
|
||||||
// Теперь A7=0, создаём отрицательный фронт iorq_n
|
write_port(16'h007F); // второй раз
|
||||||
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;
|
|
||||||
#10;
|
#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 ===");
|
$display("=== Test 2: Self-lock at value 3 ===");
|
||||||
|
write_port(16'h007F); // третий раз -> lock
|
||||||
// Инкрементируем до 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;
|
|
||||||
#10;
|
#10;
|
||||||
if (CR_ROM_oe_n !== 0) $display("ERROR: CR_ROM_oe_n = %b, expected 1 (self_lock active)", CR_ROM_oe_n);
|
check_equal(3, CR_ROM_A, "After third write (should lock)");
|
||||||
if (ZX_ROM_blk !== 1) $display("ERROR: ZX_ROM_blk = %b, expected 0", ZX_ROM_blk);
|
|
||||||
|
|
||||||
rd_n = 1; mreq_n = 1;
|
// Попытка инкремента после блокировки
|
||||||
|
write_port(16'h007F);
|
||||||
#10;
|
#10;
|
||||||
|
check_equal(3, CR_ROM_A, "Write after lock - no increment");
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
// Test 3: CR_ROM_oe_n активируется при обращении в нижние 8кб
|
// Test 3: При self_lock=1 CR_ROM_oe_n не активируется даже в нижней ROM
|
||||||
// Условия: lower_rom=1, rd_n=0, mreq_n=0, self_lock=0
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
$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;
|
reset_n = 0;
|
||||||
#10;
|
#20;
|
||||||
reset_n = 1;
|
reset_n = 1;
|
||||||
#10;
|
#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
|
// Test 5: Активация CR_ROM_oe_n при чтении нижних 8KB (self_lock=0)
|
||||||
rd_n = 0;
|
// ------------------------------------------------------------
|
||||||
mreq_n = 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;
|
#10;
|
||||||
|
|
||||||
// Ожидаем CR_ROM_oe_n = 0 (активен)
|
// mreq_n=0, rd_n=1 – чтение не активно
|
||||||
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);
|
mreq_n = 0; rd_n = 1;
|
||||||
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)
|
|
||||||
#10;
|
#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 выход отключается
|
// mreq_n=1, rd_n=0 – нет запроса памяти
|
||||||
A13 = 0; // обратно в lower_rom=1
|
mreq_n = 1; rd_n = 0;
|
||||||
rd_n = 1;
|
|
||||||
#10;
|
#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;
|
#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;
|
#10;
|
||||||
|
|
||||||
$display("=== All tests completed ===");
|
$display("=== All tests completed ===");
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user