- PVSM.RU - https://www.pvsm.ru -
Спроектируем Little Man Computer [1] на языке Verilog.
Статья [2] про LMC была на Хабре.
Online симулятор этого компьютера здесь [3].
Сперва создадим устройство, позволяющее производить загрузку данных в ОЗУ.

Напишем модуль ОЗУ на языке Verilog.
module R0 #(parameter N = 2, M = 4)
(
input clk,
input [N-1:0] adr,
input [M-1:0] data_in,
output [M-1:0] RAM_out
);
reg [M-1:0] mem [2**N-1:0];
always @(posedge clk)
mem [adr] <= data_in;
assign RAM_out = mem[adr];
endmodule
Подключим счётчик к адресному входу ОЗУ
module R1 #(parameter N = 2, M = 4)
(
input Counter_clk, RAM_clk,
//input [N-1:0] adr,
input [M-1:0] data_in,
output [M-1:0] RAM_out
);
reg [1:0]counter;
always @(posedge Counter_clk)
counter <= counter + 1;
wire [N-1:0] adr;
assign adr = counter;
reg [M-1:0] mem [2**N-1:0];
always @(posedge RAM_clk)
mem [adr] <= data_in;
assign RAM_out = mem[adr];
endmodule
Вот так выглядит схема в RTL Viewer

Добавим в счетчик функцию загрузки. Теперь мы можем командой Counter_load переходить на адрес data_in.
//input Counter_load;
wire [3:0] branch_adr; // адрес перехода
assign branch_adr = data_in;
always @(posedge Counter_clk)
begin
if(Counter_load) //по команде "Counter_load" переходим по адресу "branch_adr"
counter <= branch_adr;
else
counter <= counter + 1;
end
В отдельном модуле создаем 4bit'ный регистр (аккумулятор)
module register4
(
input [3:0] reg_data,
input reg_clk,
output reg [3:0] q
);
always @(posedge reg_clk)
q <= reg_data;
endmodule
Добавим в общую схему аккумулятор, мультиплексор и сумматор
module R2 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4)
(
input Counter_clk, Counter_load, RAM_clk,
input MUX_switch,
input Acc_clk,
input [3:0] data_in,
output [3:0] Acc,
output [DATA_WIDTH-1:0] RAM,
output reg [1:0] counter
);
wire [1:0] branch_adr;
assign branch_adr = data_in[1:0];
//Counter
always @(posedge Counter_clk)
begin
if(Counter_load)
counter <= branch_adr;
else
counter <= counter + 1;
end
wire [ADDR_WIDTH-1:0] adr;
assign adr = counter;
//RAM
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0];
always @(posedge RAM_clk)
mem [adr] <= Acc;
assign RAM = mem[adr];
//sum
wire [3:0] sum;
assign sum = Acc + RAM;
//MUX
reg [3:0] MUX2;
always @*
MUX2 = MUX_switch ? sum : data_in;
//Accumulator
register4 Acc_reg(
.reg_data(MUX2),
.reg_clk(Acc_clk),
.q(Acc)
);
endmodule

Добавим в основной модуль элемент «вычитатель»
wire [3:0] subtract;
assign subract = Acc - RAM ;
Замним двухвходовой мультиплексор четырёхвходовым
always @*
MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract)
: (MUX_switch[0] ? sum : data_in);
Подключим к аккумулятору устройство вывода (4bit'ный регистр), также подключим к аккумулятору 2 флага:
1. Флаг «Ноль» — это лог. элемент 4ИЛИ-НЕ. Флаг поднимается, если содержимое Асс равно нулю.
2. Флаг «Ноль или Положительное число» — это лог. элемент НЕ на старшем разряде четырёхразрядного аккумулятора. Флаг поднимается, если содержимое Асс больше или равно нулю.
//флаг "Ноль"
output Z_flag;
assign Z_flag = ~(|Acc); // многовходовой вентиль ИЛИ
//флаг "Ноль или Положительное число"
output PZ_flag;
assign PZ_flag = ~Acc[3];

Добавим три команды
1. загрузка содержимого аккумулятора в устройство вывода data_out
2. загрузка адреса в счётчик, если поднят флаг «ноль» (JMP if Acc=0)
3. загрузка адреса в счётчик, если поднят флаг «ноль или положительное число» (JMP if Acc>=0)
module R3 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4)
(
input Counter_clk, RAM_clk,
input JMP, Z_JMP, PZ_JMP,
input [1:0] MUX_switch,
input Acc_clk,
input Output_clk,
input [3:0] data_in,
output [3:0] Acc,
output [3:0] data_out,
output [DATA_WIDTH-1:0] RAM,
output Z_flag, PZ_flag,
output reg [1:0] counter
);
wire [1:0] branch_adr;
assign branch_adr = data_in[1:0];
wire Z,PZ;
assign Z = Z_flag & Z_JMP;
assign PZ = PZ_flag & PZ_JMP;
//Counter
always @(posedge Counter_clk)
begin
if(JMP|Z|PZ)
counter <= branch_adr;
else
counter <= counter + 1;
end
wire [ADDR_WIDTH-1:0] adr;
assign adr = counter;
//RAM
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0];
always @(posedge RAM_clk)
mem [adr] <= Acc;
assign RAM = mem[adr];
//sum
wire [3:0] sum;
assign sum = Acc + RAM;
//subtract
wire [3:0] subtract;
assign subtract = Acc - RAM;
//MUX
reg [3:0] MUX4;
always @*
MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract)
: (MUX_switch[0] ? sum : data_in);
register4 Acc_reg(
.reg_data(MUX4),
.reg_clk(Acc_clk),
.q(Acc)
);
register4 Output_reg(
.reg_data(Acc),
.reg_clk(Output_clk),
.q(data_out)
);
assign Z_flag = ~(|Acc);
assign PZ_flag = ~Acc[3];
endmodule

Поместим команды и адреса в одно ОЗУ, а данные — в другое.

Схему можно скачать отсюда [4].
В первых восьми разрядах хранятся команды, в последних четырех разрядах хранится адрес, загружаемый в счётчик.
Отмечу, что загрузка числа в аккумулятор Асс должна производиться после переключения мультиплексора MUX (для команд ADD, SUB, LDA), по спаду тактового сигнала.
Т.о. в нашем компьютере следующая система команд
48х — ADD добавить число из ОЗУ к Асс
50х — SUB вычесть число, хранящееся в ОЗУ из Асс
80x — STA сохранить число из аккумулятора Асс в ОЗУ по адресу х
58х — LDA загрузить число из адреса х в Асс
04х — BRA безусловный переход в ячейку с адресом x
02х — BRZ переход в ячейку с адресом x, если Асс=0 (условный переход)
01x — BRP переход в ячейку с адресом x, если Асс>=0 (условный переход)
40х — INP загрузить число из data_input в Асс
20х — OUT загрузить число из Асс в data_out
Команды HLT у нас не будет.
Возьмём для примера алгоритм поиска максимального из двух чисел с сайта http://peterhigginson.co.uk/LMC/ [3]
Алгоритм работает так: сохраняем в память данных два числа из data_in. Вычитаем из второго числа первое:
00 INP
01 STA 11
02 INP
03 STA 12
04 SUB 11
05 BRP 08
06 LDA 11
07 BRA 09
08 LDA 12
09 OUT
В нашей системе команд этот алгоритм будет выглядеть так
400
80b
400
80c
50b
018
58b
049
58c
200

Автор: Дмитрий
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/279576
Ссылки в тексте:
[1] Little Man Computer: https://en.wikipedia.org/wiki/Little_man_computer
[2] Статья: https://habr.com/post/257331/
[3] здесь: http://peterhigginson.co.uk/LMC/
[4] отсюда: https://yadi.sk/d/MfVCrSWB3VP5UF
[5] Источник: https://habr.com/post/354978/?utm_source=habrahabr&utm_medium=rss&utm_campaign=354978
Нажмите здесь для печати.