Icarus Verilog НЕ підтримує associative arrays!
Ця лабораторна робота містить:
- ✅ Теоретичне пояснення всіх завдань
- ✅ Правильний SystemVerilog синтаксис
- ✅ Демонстраційний код (запускається в Icarus, показує теорію)
- ❌ Реальне виконання (потребує комерційний симулятор)
Для реального запуску потрібен: Questa/ModelSim, VCS, або Xcelium.
int mac_table [string];Структура:
- Ключ:
string(MAC-адреса) - Значення:
int(номер порту) - Тип: Associative array (хеш-таблиця)
Чому це потужно:
- Динамічний розмір (росте автоматично)
- Швидкий пошук O(1)
- Будь-який тип ключа
- Економія пам'яті (тільки використані ключі)
mac_table["AA:BB:CC:DD:EE:01"] = 1;
mac_table["AA:BB:CC:DD:EE:02"] = 2;
mac_table["AA:BB:CC:DD:EE:03"] = 3;
mac_table["AA:BB:CC:DD:EE:04"] = 9;
mac_table["AA:BB:CC:DD:EE:05"] = 7;
mac_table["AA:BB:CC:DD:EE:06"] = 8;Результат:
- 6 записів додано
mac_table.num() = 6
Пояснення:
- Простий синтаксис:
array[key] = value - Автоматичне створення entry якщо ключ не існує
- Немає потреби попередньо виділяти пам'ять
// Перевірка існуючого запису
if (mac_table.exists("AA:BB:CC:DD:EE:02")) begin
$display("Знайдено: порт %0d", mac_table["AA:BB:CC:DD:EE:02"]);
end
// Вивід: ✓ MAC AA:BB:CC:DD:EE:02 ЗНАЙДЕНО → порт 2
// Перевірка неіснуючого запису
if (mac_table.exists("AA:BB:CC:DD:EE:11")) begin
$display("Знайдено: порт %0d", mac_table["AA:BB:CC:DD:EE:11"]);
end else begin
$display("MAC не знайдено");
end
// Вивід: ✗ MAC AA:BB:CC:DD:EE:11 НЕ ЗНАЙДЕНО (порт відсутній)Важливо:
⚠️ НЕ звертайтеся до масиву без перевірки exists()!Звернення до неіснуючого ключа
mac_table[key]створює новий запис зі значенням за замовчуванням (0 для int). Це може призвести до небажаних побічних ефектів!
Правильно:
if (mac_table.exists(key)) begin
value = mac_table[key]; // ✅ Безпечно
endНеправильно:
value = mac_table[key]; // ⚠️ Створює запис якщо не існує!
if (value != 0) begin // Неправильна логіка
// ...
endmac_table["AA:BB:CC:DD:EE:10"] = 5;Результат:
- Розмір таблиці:
7записів (було 6) - Нова адреса прив'язана до порту 5
mac_table.delete("AA:BB:CC:DD:EE:01");Результат:
-
Перед видаленням:
- Розмір: 7 записів
exists("AA:BB:CC:DD:EE:01")= TRUE
-
Після видалення:
- Розмір: 6 записів
exists("AA:BB:CC:DD:EE:01")= FALSE
Пояснення:
delete(key)- видаляє один конкретний записdelete()- видаляє ВСІ записи (очищає масив)
// Видалення одного запису
mac_table.delete("AA:BB:CC:DD:EE:01");
// Очищення всього масиву
mac_table.delete(); // Тепер mac_table.num() == 0// Перед оновленням: порт 7
mac_table["AA:BB:CC:DD:EE:05"] = 10;
// Після оновлення: порт 10Результат:
AA:BB:CC:DD:EE:05: порт 7 → порт 10
Пояснення:
- Простий перезапис значення
- Якщо ключ існує - оновлюється значення
- Якщо ключ не існує - створюється новий запис
string key;
if (mac_table.first(key)) begin
do begin
$display("%s → порт %0d", key, mac_table[key]);
end while (mac_table.next(key));
endРезультат:
╔════════════════════════╦═══════════╗
║ MAC Address ║ Port ║
╠════════════════════════╬═══════════╣
║ AA:BB:CC:DD:EE:02 ║ 2 ║
║ AA:BB:CC:DD:EE:03 ║ 3 ║
║ AA:BB:CC:DD:EE:04 ║ 9 ║
║ AA:BB:CC:DD:EE:05 ║ 10 ║ ← оновлено
║ AA:BB:CC:DD:EE:06 ║ 8 ║
║ AA:BB:CC:DD:EE:10 ║ 5 ║ ← додано
╚════════════════════════╩═══════════╝
↑
(AA:BB:CC:DD:EE:01 видалено)
Пояснення механізму:
first(key)- встановлюєkeyна перший ключ, повертає 1 (TRUE)next(key)- переходить до наступного ключа, повертає 1 доки є елементи- Коли досягнуто кінця,
next()повертає 0 (FALSE)
| Метод | Опис | Приклад |
|---|---|---|
num() |
Кількість елементів | int n = arr.num(); |
exists(key) |
Перевірка існування ключа | if (arr.exists(k)) |
first(key) |
Перший ключ (ініціалізує ітерацію) | arr.first(k) |
last(key) |
Останній ключ | arr.last(k) |
next(key) |
Наступний ключ | while (arr.next(k)) |
prev(key) |
Попередній ключ | while (arr.prev(k)) |
delete(key) |
Видалення одного елемента | arr.delete(k) |
delete() |
Видалення всіх елементів | arr.delete() |
string mac = "AA:BB:CC:DD:EE:02";
if (mac_table.exists(mac)) begin
int port = mac_table[mac];
$display("Знайдено: порт %0d", port);
end else begin
$display("MAC не знайдено");
endstring key;
if (mac_table.first(key)) begin
do begin
$display("%s → %0d", key, mac_table[key]);
end while (mac_table.next(key));
endstring key;
if (mac_table.last(key)) begin
do begin
$display("%s → %0d", key, mac_table[key]);
end while (mac_table.prev(key));
end// Спосіб 1: Видалити все одразу
mac_table.delete();
// Спосіб 2: Поелементне видалення
string key;
while (mac_table.first(key)) begin
mac_table.delete(key);
endint count = mac_table.num();
$display("Таблиця містить %0d записів", count);
if (mac_table.num() == 0) begin
$display("Таблиця порожня");
endAssociative array може використовувати різні типи ключів:
int data [int];
data[42] = 100;
data[-5] = 200;
data[1000000] = 300; // Sparse - економія пам'яті!int ports [string];
ports["router1"] = 10;
ports["switch2"] = 20;
ports["firewall"] = 30;string names [logic[7:0]];
names[8'hFF] = "broadcast";
names[8'h00] = "null";
names[8'h01] = "device1";typedef enum {RED, GREEN, BLUE} color_t;
int rgb_values [color_t];
rgb_values[RED] = 32'hFF0000;
rgb_values[GREEN] = 32'h00FF00;
rgb_values[BLUE] = 32'h0000FF;typedef struct {
int x;
int y;
} point_t;
string labels [point_t];
point_t p1 = '{10, 20};
point_t p2 = '{30, 40};
labels[p1] = "start";
labels[p2] = "end";┌─────────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│ │ Associative │ Dynamic │ Unpacked │ Queue │
├─────────────────┼──────────────┼──────────────┼──────────────┼──────────────┤
│ Розмір │ Динамічний │ Динамічний │ Фіксований │ Динамічний │
│ Тип індексу │ Будь-який │ Integer │ Integer │ Integer │
│ Порядок │ Hash table │ Послідовний │ Послідовний │ Послідовний │
│ Пошук │ O(1) │ O(1) │ O(1) │ O(n) │
│ Пам'ять │ Тільки викор.│ Весь розмір │ Весь розмір │ Динамічна │
│ Методи │ exists, etc. │ size, new │ Немає │ push, pop │
│ Синтез │ НІ │ НІ │ НІ │ НІ │
└─────────────────┴──────────────┴──────────────┴──────────────┴──────────────┘
Associative Array:
- ✅ Lookup tables з динамічним розміром
- ✅ Sparse data (рідко заповнені дані)
- ✅ Потрібен пошук по нечисловому ключу
- ✅ Scoreboards, cache models
Dynamic Array:
- ✅ Розмір невідомий на початку
- ✅ Послідовний доступ по індексу
- ✅ Потрібно resize
Unpacked Array:
- ✅ Фіксований розмір відомий
- ✅ Матриці, буфери
- ✅ Простий послідовний доступ
Queue:
- ✅ FIFO/LIFO структури
- ✅ Потрібні push/pop операції
- ✅ Динамічний розмір + вставка/видалення
module mac_learning_switch;
int mac_table [string]; // MAC → port mapping
task learn_mac(string mac, int port);
mac_table[mac] = port;
$display("Learned: %s on port %0d", mac, port);
endtask
function int forward_packet(string dst_mac);
if (mac_table.exists(dst_mac)) begin
$display("Forwarding to port %0d", mac_table[dst_mac]);
return mac_table[dst_mac];
end else begin
$display("MAC unknown, flooding all ports");
return -1; // Flood
end
endfunction
endmodulemodule cache_model;
logic [31:0] cache [logic[31:0]]; // address → data
int hits = 0;
int misses = 0;
task read(logic [31:0] addr, output logic [31:0] data);
if (cache.exists(addr)) begin
data = cache[addr];
hits++;
$display("Cache HIT: addr=%h, data=%h", addr, data);
end else begin
data = read_from_memory(addr); // Simulate memory read
cache[addr] = data; // Cache it
misses++;
$display("Cache MISS: addr=%h, data=%h", addr, data);
end
endtask
function void print_stats();
int total = hits + misses;
real hit_rate = real'(hits) / real'(total) * 100.0;
$display("Cache Statistics:");
$display(" Hits: %0d", hits);
$display(" Misses: %0d", misses);
$display(" Hit Rate: %.2f%%", hit_rate);
endfunction
endmoduleclass scoreboard;
typedef struct {
logic [31:0] data;
int timestamp;
bit valid;
} transaction_t;
transaction_t expected [int]; // Transaction ID → expected data
function void add_expected(int id, logic [31:0] data);
expected[id] = '{data, $time, 1};
$display("[%0t] Expected: ID=%0d, Data=%h", $time, id, data);
endfunction
task compare(int id, logic [31:0] actual);
if (!expected.exists(id)) begin
$error("Unexpected transaction: ID=%0d", id);
return;
end
if (expected[id].data == actual) begin
$display("[%0t] PASS: ID=%0d, Data=%h", $time, id, actual);
end else begin
$error("[%0t] FAIL: ID=%0d, Expected=%h, Actual=%h",
$time, id, expected[id].data, actual);
end
expected.delete(id); // Remove from scoreboard
endtask
endclassmodule sparse_memory #(
parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 8
);
logic [DATA_WIDTH-1:0] mem [logic[ADDR_WIDTH-1:0]];
task write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data);
mem[addr] = data;
$display("Write: mem[%h] = %h", addr, data);
endtask
task read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
if (mem.exists(addr)) begin
data = mem[addr];
end else begin
data = '0; // Uninitialized memory returns 0
end
$display("Read: mem[%h] = %h", addr, data);
endtask
function int get_used_size();
return mem.num(); // Тільки реально використана пам'ять!
endfunction
// Приклад: 4GB address space, але займає пам'ять тільки для записаних адрес
initial begin
write(32'h0000_0000, 8'hAA);
write(32'hFFFF_FFFF, 8'hBB);
$display("Used only %0d bytes, not 4GB!", get_used_size());
end
endmoduleiverilog -g2012 -o lab1d_unpacked_array lab1d_unpacked_array.sv
vvp lab1d_unpacked_arrayРезультат: Теоретичне пояснення (НЕ реальне виконання)
vlog -sv lab1d_unpacked_array.sv
vsim -c -do "run -all; quit" associative_array_demovcs -sverilog +v2k lab1d_unpacked_array.sv
./simvxrun -sv lab1d_unpacked_array.sv- Швидкий пошук O(1) - hash table implementation
- Економія пам'яті - тільки використані ключі займають пам'ять
- Гнучкість ключів - будь-який тип: int, string, enum, struct
- Динамічний розмір - автоматично росте при додаванні
- Зручні методи - exists(), first(), next(), delete()
- НЕ синтезується - тільки для тестбенчів!
- Потребує комерційний симулятор - Icarus Verilog не підтримує
- Непередбачуваний порядок - ітерація може відрізнятися між запусками
- Створення при зверненні - доступ до неіснуючого ключа створює запис
ДОБРЕ для:
- Lookup tables (MAC tables, routing tables)
- Sparse memory models
- Scoreboards та checkers
- Cache simulators
- Test data structures
ПОГАНО для:
- RTL дизайн (не синтезується!)
- Коли потрібен гарантований порядок
- Коли використовуєте Icarus Verilog
mac_table = {}
mac_table["AA:BB:CC:DD:EE:01"] = 1
if "AA:BB:CC:DD:EE:02" in mac_table:
port = mac_table["AA:BB:CC:DD:EE:02"]std::map<std::string, int> mac_table;
mac_table["AA:BB:CC:DD:EE:01"] = 1;
if (mac_table.find("AA:BB:CC:DD:EE:02") != mac_table.end()) {
int port = mac_table["AA:BB:CC:DD:EE:02"];
}int mac_table [string];
mac_table["AA:BB:CC:DD:EE:01"] = 1;
if (mac_table.exists("AA:BB:CC:DD:EE:02")) begin
int port = mac_table["AA:BB:CC:DD:EE:02"];
endСхожість: Всі використовують hash table для швидкого пошуку O(1).
Associative array - це потужний інструмент для верифікації SystemVerilog:
- 🔑 Key-Value структура з динамічним розміром
- ⚡ Швидкий пошук завдяки hash table
- 💾 Економія пам'яті для sparse data
- 🔧 Гнучкість у виборі типу ключа
- ✅ Ідеально для тестбенчів і scoreboards
Але пам'ятайте:
⚠️ Тільки для верифікації, НЕ для синтезу!
⚠️ Потребує комерційний симулятор!
Версія: 1.0
Дата: 19 жовтня 2025 р.
Симулятор: Icarus Verilog 12.0 (теоретичне виконання)
Рекомендовано: Questa/VCS/Xcelium (реальне виконання)
Стандарт: SystemVerilog IEEE 1800-2012