Peter j

У цьому розділі ми розглянемо засоби в VHDL для файлового введення \ виводу. Файли служать для цілого ряду цілей, одним з яких є, забезпечення зберігання довгостроковій інформації. У цьому контексті "довгострокова інформація" означає інформацію поза життям одного запуску моделювання. Файли можуть бути використані для зберігання даних, які будуть завантажені в моделі при запуску або для зберігання результатів, отриманих за допомогою моделювання. VHDL також надає спеціалізовані версії файлових операцій для роботи з текстовими файлами. Ми покажемо, як текстовий введення і виведення може бути використаний для розширення користувальницького інтерфейсу симулятора зі специфічними для моделі операціями.

16.1 Файли

Ми починаємо наше обговорення файлів з розгляду механізмів загального призначення, передбачених в VHDL для файлового введення \ виводу. VHDL забезпечує послідовний доступ до файлів за допомогою операцій, таких, як "open", "close", "read" і "write", які є звичними для користувачів традиційних мов програмування.

VHDL файл є класом об'єктів використовуються для зберігання даних. Таким чином, як і з іншими класами об'єктів, ми повинні додати визначення файлу в модель. Синтаксис для визначення типу файлу наступний:

file_type_definition. file of type_mark

type integer_file is file of integer;

визначає integer_file вказує на тип файлу, який може зберігати тільки цілі числа. Файл може містити тільки один тип об'єкта, але таким типом може бути майже будь-який тип VHDL, включаючи типи скалярних записів і одновимірних масивів. Єдині типи, які не можуть бути збережені в файл, є багатовимірні масиви, типи доступу, типи захисту та інші файли.

Після того, як ми визначили тип файлу, ми можемо оголосити файловий об'єкт. Ми робимо це за допомогою декларації об'єкта, описаної в такий спосіб:

file identifier. subtype_indication

[[Open file_open_kind_expr] is string_expression];

Визначення файлу створює один або кілька об'єктів даного типу файлу. Ми можемо додати декларацію файлу в будь-який декларативною частини, в якій ми можемо створити об'єкти, такі як: опис архітектури, блоки, процеси, пакети і підпрограми.

file lookup_table_file. integer_file is "lookup-values";

симулятор, який працює під управлінням операційної системи UNIX, може зв'язати файловий об'єкт з фізичним файлом з ім'ям "lookup-values" в поточному робочому каталозі. Інший симулятор, який працює під операційною системою Windows, може зв'язати файловий об'єкт по-різному, так як імена файлів, як правило, включають в себе розширення файлу в даній операційній системі. Так що, можна асоціювати об'єкт з фізичним файлом під назвою "lookup-values.dat" в поточному робочому каталозі.

Опціональне вираз після відкриття ключових слів дозволяє нам визначити, як фізичний файл, пов'язаний з об'єктом файлової системи повинен бути відкритий. Цей вислів має мати значення визначеного типу file_open_kind, заявленим в стандартному пакеті. Визначення має вид:

type file_open_kind is (read_mode, write_mode, append_mode);

Якщо опустити в визначенні інформацію про декларації файлу, але додавши логічне ім'я файлу, фізичний файл відкриється в режимі читання. У решти цього розділу ми розглянемо кожен з цих режимів і подивимося, як дані зчитуються і записуються за допомогою файлів, відкриті в кожному з режимів.

VHDL-87. Синтаксис визначення файлу для VHDL-87:
file_declaration.
file identifier. subtype_indication is
[In or out] string_expression;

16.1.2 Читання з файлу

Операції read і endfile описуються так:

procedure read (file f. file_type; value. out element_type);
function endfile (file f. file_type) return boolean;

Далі в цій главі ми розглянемо всі параметри файлів.

Приклад 16.1 Ініціалізація вмісту ПЗУ з файлу

Ми можемо використовувати файлові операції для ініціалізації вмісту ПЗУ (ROM) з файлу. Нижче наводиться визначення об'єкта для ROM, який містить загальну константу, яка вказує ім'я файлу, з якого має завантажитися вміст ROM.
library ieee; use ieee.std_logic_1164.all;
entity ROM is
generic (load_file_name. string);
port (sel. in std_ulogic;
address. in std_ulogic_vector;
data. inout std_ulogic_vector);
end entity ROM;

read (load_file, storage (index));
index: = index + 1;
end loop;
-- respond to ROM accesses
loop
.
end loop;
end process behavior;
end architecture behavioral;

У наведеному вище прикладі, кожен елемент файлу є standard-logic вектором фіксованої довжини, який визначається шириною порту даних ROM. Проте, ми не обмежені масивом фіксованою довжини як елементи файлів. Ми можемо оголосити тип файлу без будь-яких обмежень або частково обмеженого типу масиву для типу елемента, за умови, що тип елемента масиву є скаляром типу або повністю обмежений композитним підтипом, наприклад:
type bit_vector_file is file of bit_vector;

Дані у файлі цього типу являють собою послідовність бітових векторів, кожен з яких може бути різної довжини. Для такого файлу, операція читання займає дещо іншу форму, через те що ми не знаємо довжину наступного елемента, поки ми його не вважали. Операція неявно оголошується як
procedure read (file f. file_type;
value. out element_type; length. out natural);

ми можемо викликати операцію читання наступним чином:
read (vectors, next_vector, actual_len);

Це дозволяє нам вважати розрядний вектор довжиною до 64 біт. Якщо таке значення у файлі менше або дорівнює 64 біт, то це значення буде поміщено в крайню ліву частину next_vector, а що залишилися біти будуть незмінні. Якщо значення в файлі довше, ніж 64 біт, перші 64 біта значення поміщаються в next_vector, а що залишилися біти відкидаються. В обох випадках actual_len встановлено в значення фактичної довжини файлу, будь воно коротше або довше, ніж довжина другого аргументу для читання. Це дозволяє нам перевірити, чи була втрачена інформація. якщо вираз
actual_len> next_vector'length

істинно, то вектор недостатньою довжини, щоб вмістити все біти.

Приклад 16.2 Читання значень з файлу

Припустимо, ми розробили модель для мережевого приймача і хочемо перевірити її. Ми можемо генерувати мережеві пакети, щоб стимулювати модель шляхом зчитування пакетів змінної довжини з файлу. Схема процесу для цього виглядає наступним чином:


stimulate_network. process is
type packet_file is file of bit_vector;
file stimulus_file. packet_file
open read_mode is "test packets";
variable packet. bit_vector (1 to 2048);
variable packet_length. natural;
begin
while not endfile (stimulus_file) loop
read (stimulus_file, packet, packet_length);
if packet_length> packet'length then
report "stimulus packet too long - ignored"
severity warning;
else
for bit_index in 1 to packet_length loop
wait until stimulus_clock;
stimulus_network 0);
.
begin
. -- initialize the instruction set interpreter
instruction_loop. loop
. -- fetch the next instruction into IR
-- decode the instruction
opcode_number: = convert_to_natural (opcode);
counters (opcode_number): = counters (opcode_number) +1;
.
-- execute the decoded instruction
case opcode is
.
when halt_opcode => exit instruction_loop;
.
end case;
end loop instruction_loop;
for index in counters'range loop
write (instruction_counts, counters (index));
end loop;
wait; - program finished, wait forever
end process interpreter;
end architecture instrumented;

Якщо файл з таким ім'ям вже існує в файлової системі і відкритий для запису, то послідовні елементи даних будуть додані в кінець файлу за допомогою операції записи. Якщо в системі немає файлу з такою назвою, то створюється новий файл з режимом для запису, і елементи даних записуються з самого початку. Режим додавання використовується для файлів, які накопичують інформацію або результати моделювання протягом ряду моделювань. Кожен прогін додає свої дані в кінці раніше накопичених даних в файл.