Проектирование счетчика инструкций, памяти инструкций и памяти данных
Далее разместим на поле счетчик инструкций PC, АЛУ (ALU) и регистровый файл (RegFile). Проведем соединения адресных линий от счетчика к ПЗУ. Подключение осуществить с применением элемента "Разветвитель". Которому задать свойства:
- "Внешний вид" - леворукий;
- "Веерный выход" - 1;
- "Разрядность входа" - 32;
- Бит 0 - нет, Бит 1 - нет;
- Бит с 16 и до 32 - нет.
Выход АЛУ также через разветвитель подключить к входу адреса ОЗУ. Единственное изменить настройки таким образом, чтобы с ОЗУ соединялись биты со 2 по 9, что соответствует 256 ячейкам памяти.
Выход данных rs2 регистрового файла подключим к входу D ОЗУ.
Добавим на поле мультиплексор с разрядностью 32 и свойством "Выбирающие биты" 1. Вход 0 мультиплексора подключим к выходу АЛУ. Второй вход подключить к выходу D ОЗУ. Выход мультиплексора подключить к входу данных регистрового файла DataIn. Организация такой схемы как раз обусловлена тем, что сначала данные загружаются в регистры, выполняется соответствующая операция, затем данные загружаются в память. Также данные из памяти загружаются в регистры, а потом в память.
За данный процесс отвечает дешифратор инструкций и блок управления.
На рисунке 9.7 показан результат объединения счетчика инструкций PC и памяти инструкций (IMEM). На рисунке 9.8 приведена схема включения операционного блока с памятью данных.
Для проектирования регистрового файла в среде Quartus, в существующем проекте RISC_V, создать новый Verilog HDL File. Сохранить файл как PCUnit.v.
Проектируемый в целом отражает функциональную схему, приведенную на рисунке 9.1, но в него уже интегрированы входы переходов с блока управления ветвлениями, который будет рассмотрен далее. В листинге 9.1 приведен код счетчика инструкций.
module PCUnit( input clk, // тактовый сигнал input reset, // сигнал сброса input branchTaken, // сигнал ветвления input [31:0] branchAddr, // адрес для перехода при ветвлении output reg [31:0] pc // выход счетчика ); // Инициализация PC стартовым адресом initial pc = 32'b0; // анализ сигнала clk по фронту always @(posedge clk) begin if (reset) begin // Установка PC в начальный адрес при сбросе pc <= 32'b0; end else if (branchTaken) begin // Если присутсвует сигнал перехода то счетчик указывает на адрес branchAddr pc <= branchAddr; end else begin // иначе PC увеличивается на 4 pc <= pc + 4; end end endmoduleЛистинг 9.1.
На рисунке 9.9 приведен вид RTL созданного модуля.
Оперативное запоминающее устройство, выполняющее функции памяти данных, создаваемая на Verilog HDL имеет аналогичные порты адреса и данных, а также входы управления записью и чтением. Для получения памяти данных в проекте создадим файл DataMemory.v и введем код из листинга 9.2.
module DataMemory ( input clk, //синхросигнал input [31:0] addr, // шина адреса input [31:0] writeData, //входная шина данных input memWrite, // сигнал управления записи input memRead, //сигнал управления чтением output [31:0] readData //выход данных ); reg [7:0] mem [0:255];// объем памяти равен 256 байт // Чтение данных assign readData = memRead ? {mem[addr+3], mem[addr+2], mem[addr+1], mem[addr]} : 32'b0; // здесь данные как раз объединаются с применением конкатенации // Запись данных по фронту clk always @(posedge clk) begin if (memWrite) begin //здесь данные из 32-х битного слова делятся на 4 байта mem[addr] <= writeData[7:0]; mem[addr+1] <= writeData[15:8]; mem[addr+2] <= writeData[23:16]; mem[addr+3] <= writeData[31:24]; //здесь данные из 32-х битного слова делятся на 4 байта end end endmoduleЛистинг 9.2.
На рисунке 9.10 приведен вид RTL созданного модуля.
Для процессора требуется описать память инструкций. На данном моменте требуется отметить, что память инструкций - обычно модуль ROM. Данный модуль содержит код, записанный заранее. Данная память может содержать код программы в виде констант с заранее определенными адресами:
always @ (addr) case (addr) 32'h00000000: instr <= 32'b001000_00000_10000_0000000011111111; 32'h00000004: instr <= 32'b101011_00000_10000_1111000000000000;
Второй вариант применение файла инициализации, чтобы загрузить данные в модуль ROM из внешнего текстового файла. Это применяется как правило для загрузки больших таблиц кода.
Пример кода для данного метода имеет вид:
initial begin $readmemh("rom_data.hex", rom); End
Для первичной проверки работоспособности проектируемого процессора будет создана память инструкций, содержащая код программы непосредственно внутри модуля.
Для получения памяти инструкций в проекте создадим файл InstructionMemory.v и введем код из листинга 9.3.
module InstructionMemory( input [31:0] addr, // шина адреса output [31:0] instr // шина данных выхода ); reg [31:0] mem [0:63]; // объем памяти 64 инструкции по 32 бит initial begin // Пример инструкций mem[0] = 32'h00000013; // ADDI r1, r0, 0 (NOP) mem[1] = 32'h002081B3; // ADD x3, x1, x2 mem[2] = 32'h40400013; // ADDI x1, x0, 64 end assign instr = mem[addr[31:2]]; // Выравнивание по 4 байтам endmoduleЛистинг 9.3.
На рисунке 9.11 приведен вид RTL созданного модуля.