Search
Write a publication
Pull to refresh
19
0
Дмитрий @demsp

User

Send message

Планиметрия (прямая и окружность)

Reading time5 min
Views7.5K
Задача 1.3
Задача 1.4
Задача 1.5
Задача 1.6
Задача 1.7

Планиметрия изучается в начальном курсе геометрии и зачастую сводится к решению практических задач без изучения теоретической базы.

В данной статье приводятся некоторые решения задач первого раздела приложения Euclidea (геометрические построения с помощью циркуля и линейки).
Читать дальше →

Сравнение алгоритмов сортировки обменами

Reading time29 min
Views6.8K

В данной статье рассматриваются различные варианты сортировки обменами, а также даётся описание простого графического приложения processing.js с примерами сортировок.

Пузырьковая сортировка


Простейший вариант: перебирать массив от первого элемента к последнему, меняя местами (если потребуется) соседние элементы.

→ Проверить здесь

Читать дальше →

К статье о приближениях

Reading time7 min
Views8.1K
Часть I
Часть II

В данной статье рассматривается метод оценок диапазона принимаемых значений и связь этого метода с задачами, содержащими модуль.


При решении некоторых задач необходимо рассматривать диапазон, в пределах которого может находиться искомая величина.

Рассмотрим метод оценок при решении неравенств.
Дать оценку сверху означает определить максимальное значение, которое может принимать искомая величина.

Предположим, что цена за одну единицу товара может колебаться в пределах от 5 до 10 RUB. Для двух единиц товара оценка сверху составляет 10+10=20 RUB, оценка снизу 5+5=10 RUB.

Рассмотрим задачу из задачника профильной направленности М.И. Башмакова
37. Известны оценки для переменных $ x $ и $ y: 0<x<5, 2<y<3.$

Дайте оценки сверху для следующих выражений:
1. $ 2x+3y $
2. $ xy $

5. $ \frac{ 1 }{y} $
6. $ \frac{ x }{y} $

8. $ x-y $
9. $ 3x-2y $

Указание к решению задач
Для оценки этих значений необходимо воспользоваться следующим свойством числовых неравенств:
Если $a<b$ и оба числа положительны, то $ -a>-b $
Если $a<b$ и оба числа положительны, то $ \frac{ 1 }{a}>\frac{ 1 }{b}$
При умножении членов неравенства на одно и то же положительное число смысл неравенства не меняется, при умножении членов неравенства на одно и то же отрицательное число смысл неравенства меняется на противоположный.
Доказательство (Элементарная математика).
Пусть $a>b$, тогда $a-b>0$. Если $m>0$, то $m(a-b)>0$, так как произведение положительных чисел положительно. Раскрыв скобки в левой части последнего неравенства, получим $am-bm>0$, т.е. $am>bm$. Аналогичным образом рассматривается случай $m<0$.
Точно такой же вывод можно сделать и относительно деления частей неравенства на какое-либо отличное от нуля число, так как деление на число $n \neq 0$ равносильно умножению на число $1/n$, а числа $n$ и $1/n$ имеют одинаковые знаки.


Читать дальше →

RAM-машина

Reading time4 min
Views14K

Часть I
Часть II
Часть III
Часть IV


На данном ресурсе уже была опубликована статья, посвящённая RAM-машине
Wikipedia также содержит статью об этой вычислительной модели


RAM-машина, которая упоминается в книге "Построение и анализ вычислительных алгоритмов" (авторы: Ахо, Хопкрофт, Ульман) имеет ограниченный набор арифметических команд (сложение, вычитание, умножение, деление), команду безусловного перехода, две команды условных переходов. Здесь же из арифметических команд присутствуют только сложение и вычитание, команды ветвления (переходов) будут идентичными командам, приведённым в книге


LIttle Man Computer ссылка обладает схожим набором команд
Online симулятор LMC находится здесь


Схема АЛУ представлена ниже.
Шина соединяет память данных с сумматором (обозначен плюсом), вычитателем (обозначен минусом) и мультиплексором MUX.
Переключение между сумматором и вычитателем производится мультиплексором.
Также в мультиплексор загружаются числовые данные из устройства ввода data_input (в физическом воплощении — это набор переключателей).


Флаги состояния Acc>=0 и Acc=0, а также устройство вывода подключены к выводу аккумулятора


image

Отличием LIttle Man Computer от RAM-машины является механизм, обеспечивающий косвенную адресацию (возможность работать с числом, хранящемся в памяти, как с адресом).


Для того, чтобы работать с числом, хранящимся в памяти, как с адресом, подключим к адресному входу Памяти Данных мультиплексор MUX, осуществляющий выборку между, собственно, адресом (поступающим из Памяти Команд) и числом, представляющем адрес и хранящемся в Памяти Данных.


Читать дальше →

Низкоуровневый эзотерический язык

Reading time16 min
Views6.9K
Эта статья о трансляторе эзотерического языка на TurboAssembler'e (TASM).
P′′ — низкоуровневый язык программирования, созданный в 1964 году Коррадо Бёмом.
Этот язык разрабатывался для реализации циклов без использования оператора GOTO.
В данной статье демонстрируется создание некого транслятора на низком уровне, в котором обработка текста программы (строки) производится посредством условных и безусловных переходов, т.е. низкоуровневых эквивалентов оператора GOTO.

Вот здесь лежит форк визуализатора(visualizer), позволяющего выполнить отладку bf-программ в пошаговом режиме

Машина, которой управляют команды транслятора, состоит из упорядоченного набора ячеек и указателя текущей ячейки, подобно тому, как организована машина Тьюринга. Кроме того, подразумевается устройство общения с внешним миром (см. команды. и ,) через поток ввода и поток вывода.


> перейти к следующей ячейке
< перейти к предыдущей ячейке
+ увеличить значение в текущей ячейке на 1
- уменьшить значение в текущей ячейке на 1
. напечатать значение из текущей ячейки
, ввести значение и сохранить в текущей ячейке
[ если значение текущей ячейки ноль, перейти вперёд по тексту программы на символ, следующий за соответствующей ] (с учётом вложенности)
] если значение текущей ячейки не нуль, перейти назад по тексту программы на символ [ (с учётом вложенности)


Сперва напишем транслятор на каком-нибудь высокоуровневом языке, например, на Паскале.

Пусть массив data_arr представляет память данных (ленту Тьюринга), пусть строка str_arr содержит команды.

Напишем программу, выводящую символ, ascii-код которого соответствует количеству + (поэтому нам нужны будут только команды + и .)

var
 data_arr:array[1..10] of integer;    // массив данных
 str_arr: string;                     // команды  
 i, j: integer;                       // индексы строки и массива
begin
 j:=1;                  // нумерация элементов массива начинается с единицы
 readln(str_arr);       //считываем строку

 for i:=1 to length(str_arr) do begin    // в цикле обрабатываем строку
  if (str_arr[i]='+') then data_arr[j]:= data_arr[j]+1;
  if (str_arr[i]='.') then write(chr(data_arr[j]));
 end;
end.

bf-код +++++++++++++++++++++++++++++++++. выдаст ! (ascii-код символа ! равен 33 ).

Программу можно проверить в online ide ideone.com
Читать дальше →

Решение задачи о приближении иррациональных

Reading time36 min
Views19K
В данной статье рассматриваются различные методы приближений иррациональных чисел, а также попутно затрагиваются вопросы, косвенно связанные с этой темой



В сборнике Арнольда есть следующая задача

38. Вычислить сумму:

$\frac{ 1 }{ 1\cdot2 } + \frac{ 1 }{ 2\cdot3 } + \frac{ 1 }{ 3\cdot4 } + ... + \frac{ 1 }{ 99\cdot100 }$


(с ошибкой не более 1% от ответа)

Ниже представлен алгоритм для вычисления частичных сумм этого ряда на языке Scheme (Lisp), который позволяет производить вычисления в обыкновенных дробях

#lang racket
(define series_sum
 ( lambda (n)
  (if (= n 0) 0
    (+ (/ 1 (* n (+ n 1))) (series_sum(- n 1)))
  ) ) )
(series_sum 10)
(series_sum 100)
(series_sum 1000)
(series_sum 10000)
(series_sum 100000)
(series_sum 1000000)

(define series_sum_1
 ( lambda (n)
  (if (= n 0) 0 
    (+ (/ 1.0 (* n (+ n 1.0))) (series_sum_1(- n 1.0)))
  ) ) )
(series_sum_1 10)
(series_sum_1 100)
(series_sum_1 1000)
(series_sum_1 10000)
(series_sum_1 100000)
(series_sum_1 1000000)

Читать дальше →

Проектирование процессора ModelSim

Reading time17 min
Views17K

Часть I
Часть II
Часть III
Часть IV


Это полная версия предыдущей статьи, к которой добавлены тестбенчи.


Спроектируем Little Man Computer на языке Verilog.


Статья про LMC была на Хабре.


Online симулятор этого компьютера здесь.


Напишем модуль оперативной памяти (ОЗУ), состоящий из четырех (ADDR_WIDTH=2) четырёхбитных (DATA_WIDTH=4) слов. Данные загружаются в ОЗУ из data_in по адресу adr при поступлении тактового сигнала clk.


module R0 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4)
(
    input clk, //тактовый сигнал
    input [ADDR_WIDTH-1:0] adr, //адрес
    input [DATA_WIDTH-1:0] data_in, //порт ввода данных
    output [DATA_WIDTH-1:0] RAM_out //порт вывода данных
);
    reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; //объявляем массив mem

    always @(posedge clk) //при поступлении тактового сигнала clk 
        mem [adr] <= data_in; //загружаем данные в ОЗУ из data_in 

    assign RAM_out = mem[adr]; //назначаем RAM_out портом вывода данных
endmodule
```<cut/>
В тестбенче загрузим 0001 по адресу 00, 0010 по адресу 01, 0100 по адресу 10, 1000 по адресу 11:
<!--<spoiler title="Создание тестбенча">-->

Создать новый проект, создать файлы R0.v и tR0.v (эти файлы автоматически добавятся к проекту). 
Скомпилировать оба файла.
Запустить моделирование скомпилированного файла tR0.v

<!--</spoiler>-->```verilog
module tR0; 
   reg clk; 
   reg [1:0] adr;
   reg [3:0] data_in;
   wire [3:0] RAM_out;
R0 test_R0 (clk, adr, data_in,RAM_out); 
initial 
   begin
    clk = 0;
    adr[0] = 0;    
    adr[1] = 0;    
    data_in[0] = 0;
    data_in[1] = 0;
    data_in[2] = 0;
    data_in[3] = 0;
    #5 data_in[0] = 1;
    #5 clk = 1;
    #5 adr[0] = 1;  data_in[0] = 0; data_in[1] = 1;  clk = 0;  
    #5 clk = 1;
    #5 adr[0] = 0; adr[1] = 1;  data_in[1] = 0; data_in[2] = 1;  clk = 0;  
    #5 clk = 1;
    #5 adr[0] = 1; adr[1] = 1;  data_in[2] = 0; data_in[3] = 1;  clk = 0;  
        #5 clk = 1;
        #5 adr[0] = 0; adr[1] = 0; data_in[3] = 0;  clk = 0;  
    #5 adr[0] = 1; adr[1] = 0;
    #5 adr[0] = 0; adr[1] = 1;
    #5 adr[0] = 1; adr[1] = 1;
    #5 adr[0] = 0; adr[1] = 0;
    #5 adr[0] = 1; adr[1] = 0;
    #5 adr[0] = 0; adr[1] = 1;
    #5 adr[0] = 1; adr[1] = 1;
    #5 adr[0] = 0; adr[1] = 0;
    #5 adr[0] = 1; adr[1] = 0;
    #5 adr[0] = 0; adr[1] = 1;
    #5 adr[0] = 1; adr[1] = 1;
end
endmodule 


Подключим счётчик к адресному входу ОЗУ. На вход счётчика необходимо подключить тактовый генератор.


Вот пример программы, использующей внутренний генератор ALTUFM_OSC. Частота штатного генератора 5.5 МГц (MAX II EPM240 CPLD Minimal Development Board).


module inner_Clock ( output reg LED);
ALTUFM_OSC osc( .oscena(1'b1), .osc(clk));
   reg signal;
   reg [24:0] osc_counter; 
   reg [24:0] const_data = 25'b10110111000110110000000; 
initial
   begin
      signal = 1'b0;
     osc_counter = 25'b0;
   end
//досчитываем до 6 000 000 и обнуляем счетчик osc_counter
always @(posedge clk)
   begin  
      osc_counter <= osc_counter+ 1'b1;
      if(osc_counter == const_data)
         begin
            signal <= ~signal;
           osc_counter <= 25'b0;
        end
LED = signal; // LED мигает ~1 раз в сеунду.
end
endmodule

Вообще, в данной схеме можно использовать внешний генератор меандра, например КМОП таймер 555 (работающий от 3.3V) или микросхему К155ЛА3 (К155ЛА3 представляет 4 логических элемента 2И-НЕ).


Подключим таймер 555 к счётчику, подключим счётчик к адресному входу ОЗУ.


Теперь при поступлении тактового сигнала на счётчик будет происходить переход на следующую ячейку в памяти. На тактовый вход ОЗУ подключим кнопку RAM_button — данные в ОЗУ будут загружаться при нажатии на эту кнопку.


module R1 (timer555, RAM_button, data_in, RAM_out, counter);
   parameter ADDR_WIDTH = 2;
   parameter DATA_WIDTH = 4;

   input timer555;
   input RAM_button;
   //input [ADDR_WIDTH-1:0] adr;
   input [DATA_WIDTH-1:0] data_in;
   output [DATA_WIDTH-1:0] RAM_out;
   output reg [1:0] counter;
// Counter
always @(posedge timer555) 
  counter <= counter + 1; 
// RAM 
 wire [ADDR_WIDTH-1:0] adr;
 assign adr = counter; 
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0];
  always @(posedge RAM_button)
     mem [adr] <= data_in;
assign RAM_out = mem[adr];
endmodule

Вот так выглядит схема в RTL Viewer



В симуляторе ModelSim эта схема работать не будет, так как симулятору не известно начальное значение регистров counter[1:0].
Работоспособность схемы можно проверить, непосредственно загрузив программу в ПЛИС.


Далее, добавим в счетчик функцию загрузки. Загрузка из data_in [1:0] производится нажатием на кнопку Counter_load


module R2 (counter, timer555, Counter_load, RAM_button, data_in, RAM_out);
   parameter ADDR_WIDTH = 2;
   parameter DATA_WIDTH = 4;

   output [1:0] counter;
   input timer555, Counter_load;
   // input [N-1:0] adr; 
   input RAM_button;
   input [DATA_WIDTH-1:0] data_in;
   output [DATA_WIDTH-1:0] RAM_out;
// Counter
reg [1:0] counter;
always @ (posedge timer555 or posedge Counter_load)
  if (Counter_load)
       counter <= data_in[1:0];  
  else
     counter <= counter + 2'b01;
// RAM 
 wire [ADDR_WIDTH-1:0] adr;
    assign adr = counter;
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM_button) 
        mem [adr] <= data_in;
assign RAM_out = mem[adr]; 
endmodule

Вот так выглядит подключение кнопок и светодиодов в Pin Planner'е:



Загрузим 0001 по адресу 00, 0010 по адресу 01, 0100 по адресу 10, 1000 по адресу 11


module tR2;
parameter ADDR_WIDTH = 2;
parameter DATA_WIDTH = 4;

reg timer555, Counter_load, RAM_button;
wire [1:0] counter;
reg [DATA_WIDTH-1:0] data_in;
wire [DATA_WIDTH-1:0] RAM_out;

R2 test_R2(counter, timer555, Counter_load, RAM_button, data_in, RAM_out);
initial // Clock generator
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end
initial 
  begin
    data_in[0] = 0;
    data_in[1] = 0;
    data_in[2] = 0;
    data_in[3] = 0;
        Counter_load = 0;
    RAM_button = 0;
    #5 data_in[0]=0; data_in[1]=0; Counter_load=1; RAM_button=0; 
        #5 data_in[0]=1; data_in[1]=0; Counter_load=0; RAM_button=1; 
    #5 data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=0; 

    #5 data_in[0]=1; data_in[1]=0; Counter_load=1; RAM_button=0; 
    #5 data_in[0]=0; data_in[1]=1; Counter_load=0; RAM_button=1; 
    #5 data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=0; 

    #5 data_in[0]=0; data_in[1]=1; Counter_load=1; RAM_button=0; 
    #5 data_in[2]=1; data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=1; 
        #5 data_in[2]=0; data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=0; 

        #5 data_in[0]=1; data_in[1]=1; Counter_load=1; RAM_button=0; 
    #5 data_in[3]=1; data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=1; 
    #5 data_in[3]=0; data_in[0]=0; data_in[1]=0; Counter_load=0; RAM_button=0; 
  end
endmodule    


В отдельном модуле создаем 4bit'ный регистр (аккумулятор).


Данные загружаются в регистр при нажатии на кнопку reg_button:


module register4
(
  input  [3:0] reg_data,
  input reg_button,
  output reg [3:0] q  
);
always @(posedge reg_button)
         q <= reg_data;
endmodule

Добавим в общую схему аккумулятор Acc, мультиплексор MUX2 и сумматор sum.
Сумматор прибавляет к числу в аккумуляторе Acc числа из памяти.
На сигнальные входы мультиплексора подаются числа data_in и sum.
Число из MUX2 загружается в аккумулятор Acc при нажатии кнопки Acc_button.
Число из Асс загружается в ОЗУ при нажатии кнопки RAM_button.




module R3 (MUX_switch, Acc_button, Acc, counter, timer555, 
                 Counter_load, RAM_button, data_in, RAM_out);
   parameter ADDR_WIDTH = 2;
   parameter DATA_WIDTH = 4;

   input MUX_switch;
   input Acc_button; 
   output [3:0] Acc;

   input timer555, Counter_load;
   output [1:0] counter;
   // input [N-1:0] adr; 
   input RAM_button;
   input [DATA_WIDTH-1:0] data_in;
   output [DATA_WIDTH-1:0] RAM_out;
// Counter
reg [1:0] counter;
always @ (posedge timer555 or posedge Counter_load)
  if (Counter_load)
       counter <= data_in[1:0];  
  else
     counter <= counter + 2'b01;
// RAM 
 wire [ADDR_WIDTH-1:0] adr;
    assign adr = counter;
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM_button) 
        mem [adr] <= Acc;
assign RAM_out = mem[adr];
// sum 
wire [3:0] sum;
assign sum =  Acc + RAM_out;
// MUX2
reg [3:0] MUX2; 
always @*
MUX2 = MUX_switch ? sum : data_in;
//Схема подавления дребезга контактов кнопки Acc_button
/* reg Acc_dff;
always @(posedge Acc_button or negedge timer555)
        if (!timer555)
            Acc_dff <= 1'b0;
        else
            Acc_dff <= timer555;  */
//Acc
register4 Acc_reg(
    .reg_data(MUX2),
        //.reg_button(Acc_dff),
    .reg_button(Acc_button),
    .q(Acc)
);
endmodule

Для программного подавления дребезга можно применить простую схему


/* reg Acc_dff;
always @(posedge Acc_button or negedge timer555)
if (!timer555)
Acc_dff <= 1'b0;
else
Acc_dff <= timer555; */

А можно использовать одновибратор (ждущий мультивибратор), собранный на таймере 555 или микросхеме К155ЛА3


Далее, будем складывать числа, например, 2 и 3.


  1. Загружаем числа в ОЗУ
  2. Обнуляем Асс
  3. Переключаем MUX2
  4. Загружаем первое число из ОЗУ в Асс
  5. Прибавляем к числу в Асс второе число из ОЗУ
  6. Загружаем сумму в ОЗУ
    module tR3;
    parameter ADDR_WIDTH = 2;
    parameter DATA_WIDTH = 4;
      reg MUX_switch;
      reg Acc_button; 
      wire [3:0] Acc;
      reg timer555, Counter_load, RAM_button;
      wire [1:0] counter;
      reg [DATA_WIDTH-1:0] data_in;
      wire [DATA_WIDTH-1:0] RAM_out;
    R3 test_R3(MUX_switch, Acc_button, Acc, counter, timer555, 
                Counter_load, RAM_button, data_in, RAM_out);
    initial 
    begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
    end
    initial 
    begin
    data_in[0] = 0;
    data_in[1] = 0;
    data_in[2] = 0;
    data_in[3] = 0;
    Counter_load = 0;
    Acc_button = 0;
    RAM_button = 0;
    MUX_switch = 0;
    #5 Counter_load = 1;
    #5 data_in[0]=0; data_in[1]=1; Counter_load = 0;
    #5 Acc_button = 1;  
    #5 RAM_button = 1;  
    #5 data_in[0]=0; data_in[1] = 0; Acc_button = 0; RAM_button = 0; 
    #5 data_in[0]=1; data_in[1]=1;
    #15 Acc_button = 1;
    #5 RAM_button = 1;
    #5 Acc_button = 0;
    #5 data_in[0]=0; data_in[1] = 0; RAM_button = 0;
    #10 Acc_button = 1;
    #10 Acc_button = 0;
    #60 MUX_switch = 1; 
    #10 Acc_button = 1;
    #10 Acc_button = 0;
    #30 Acc_button = 1;
    #10 Acc_button = 0;
    #30 RAM_button = 1;
    #10 RAM_button = 0;
    end
    endmodule    


Добавим в основной модуль элемент, вычитающий из числа в аккумуляторе число, записанное в памяти.


wire [3:0] subtract;
assign subract =  Acc - RAM_out ;

Заменим двухвходовой мультиплексор 4х_входовым```verilog
always @*
MUX4 = MUX_switch[1]? (MUX_switch[0]? RAM_out: subtract)
: (MUX_switch[0]? sum: data_in);


Подключим к аккумулятору устройство вывода (4bit'ный регистр), также подключим к аккумулятору  2 флага:

1. Флаг "Ноль" - это лог. элемент 4ИЛИ-НЕ.  Флаг поднимается, если содержимое Асс равно нулю. 

2. Флаг "Ноль или Положительное число" - это лог. элемент НЕ на старшем разряде четырёхразрядного аккумулятора. Флаг поднимается, если содержимое Асс больше или равно нулю.
```verilog
//флаг "Ноль" 
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 R4 (JMP,Z_JMP,PZ_JMP,Z_flag,PZ_flag,Output_button,data_out,MUX_switch,Acc_button,Acc,counter,timer555,RAM_button,data_in,RAM_out);
    parameter ADDR_WIDTH = 2;
    parameter DATA_WIDTH = 4;
    
    input JMP, Z_JMP, PZ_JMP;
    output Z_flag, PZ_flag;
    input Output_button;
    output [3:0] data_out;
    input [1:0] MUX_switch;
    input Acc_button; 
    output [3:0] Acc;
    input timer555; 
    output [1:0] counter;
    input RAM_button;
    input [DATA_WIDTH-1:0] data_in;
    output [DATA_WIDTH-1:0] RAM_out;
    // flags
    wire Z,PZ;
    assign Z = Z_flag & Z_JMP;
    assign PZ = PZ_flag & PZ_JMP;   
    // Counter
    reg [1:0] counter;
    always @ (posedge timer555 or posedge JMP or posedge Z or posedge PZ)
    if (JMP|Z|PZ)
       counter <= data_in[1:0];  
    else
     counter <= counter + 2'b01;
    // RAM 
    wire [ADDR_WIDTH-1:0] adr;
    assign adr = counter;
    reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM_button) 
        mem [adr] <= Acc;
    assign RAM_out = mem[adr];
    // sum 
    wire [3:0] sum;
    assign sum =  Acc + RAM_out;
    //subtract
    wire [3:0] subtract;
    assign subtract =  Acc - RAM_out;
    // MUX4
    reg [3:0] MUX4; 
    always @*
    MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM_out : subtract)
    : (MUX_switch[0] ? sum : data_in);
    //Acc
    register4 Acc_reg(
    .reg_data(MUX4),
    .reg_button(Acc_button),
    .q(Acc)
    );
    //data_out
    register4 Output_reg(
    .reg_data(Acc),
    .reg_button(Output_button),
    .q(data_out)
    );
    assign Z_flag =  ~(|Acc);
    assign PZ_flag =  ~Acc[3];
    endmodule


  4. Загружаем числа в ОЗУ


  5. Обнуляем Асс


  6. Переключаем MUX2


  7. Вычитаем первое число (записанное в ОЗУ) из Асс


  8. Вычитаем второе число (записанное в ОЗУ) из Асс


  9. Загружаем сумму в ОЗУ и в data_out


    
    module tR4;
    parameter ADDR_WIDTH = 2;
    parameter DATA_WIDTH = 4;
    reg JMP, Z_JMP, PZ_JMP;
    wire Z_flag, PZ_flag;
    reg Output_button;
    wire [3:0] data_out;
    reg [1:0] MUX_switch;
    reg Acc_button; 
    wire [3:0] Acc;
    reg timer555, RAM_button;
    wire [1:0] counter;
    reg [DATA_WIDTH-1:0] data_in;
    wire [DATA_WIDTH-1:0] RAM_out;


R4 test_R4
(JMP,Z_JMP,PZ_JMP,Z_flag,PZ_flag,Output_button,data_out,MUX_switch,Acc_button,Acc,
counter,timer555,RAM_button,data_in,RAM_out);
initial
begin
timer555 = 0;
forever #20 timer555 = ~timer555;
end
initial
begin
data_in[0] = 0;
data_in[1] = 0;
data_in[2] = 0;
data_in[3] = 0;
JMP = 0; Z_JMP = 0; PZ_JMP = 0;
Acc_button = 0;
RAM_button = 0;
Output_button = 0;


MUX_switch[0] = 0;
MUX_switch[1] = 0;
    #5 JMP = 1;
#5 data_in[0]=0; data_in[1]=1; JMP = 0;
#5 Acc_button = 1;  
#5 RAM_button = 1;  
#5 data_in[0]=0; data_in[1] = 0; Acc_button = 0; RAM_button = 0; 
#5 data_in[0]=1; data_in[1]=1;
#15 Acc_button = 1;
#5 RAM_button = 1;
#5 Acc_button = 0;
#5 data_in[0]=0; data_in[1] = 0; RAM_button = 0;
#10 Acc_button = 1;
#10 Acc_button = 0;
#60 MUX_switch[1] = 1; 
#10 Acc_button = 1;
#10 Acc_button = 0;
#30 Acc_button = 1;
#10 Acc_button = 0;
#30 RAM_button = 1; Output_button = 1;
#10 RAM_button = 0; Output_button = 0;

end
endmodule


<img src="https://habrastorage.org/webt/jb/fq/c4/jbfqc45piovx4besdmw0pkxz_oa.gif" />

Проверим, что когда в Асс лежит положительное число, перехода Z_JMP не происходит:
```verilog
module tR4_jmp;
parameter ADDR_WIDTH = 2;
parameter DATA_WIDTH = 4;

   reg JMP, Z_JMP, PZ_JMP;
   wire Z_flag, PZ_flag;
   reg Output_button;
   wire [3:0] data_out;
   reg [1:0] MUX_switch;
   reg Acc_button; 
   wire [3:0] Acc;
   reg timer555, RAM_button;
   wire [1:0] counter;
   reg [DATA_WIDTH-1:0] data_in;
   wire [DATA_WIDTH-1:0] RAM_out;
R4 test_R4
(JMP,Z_JMP,PZ_JMP,Z_flag,PZ_flag,Output_button,data_out,MUX_switch,Acc_button,Acc,
counter,timer555,RAM_button,data_in,RAM_out);
initial 
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end
initial 
  begin
    data_in[0] = 0;
    data_in[1] = 0;
    data_in[2] = 0;
    data_in[3] = 0;
        JMP = 0; Z_JMP = 0; PZ_JMP = 0;
    Acc_button = 0;
    RAM_button = 0;
    Output_button = 0;

    MUX_switch[0] = 0;
    MUX_switch[1] = 0;
        #5 JMP = 1;
    #5 data_in[0]=0; data_in[1]=1; JMP = 0;
    #5 Acc_button = 1;  
    #5 data_in[0]=1; data_in[1]=1; Acc_button = 1;  
    #5 data_in[0]=1; data_in[1]=1; Acc_button = 0;  
    #5 Z_JMP = 1;
    #5 PZ_JMP = 1; Z_JMP = 0;
    #5 PZ_JMP = 0; 
  end
endmodule    


Поместим команду безусловного перехода в ОЗУ



Конструкция вида


//wire Counter_load;
always @ (posedge timer555)
  if (Counter_load)
       counter <= RAM_out[3:0];  
  else
     counter <= counter + 2'b01;

в ModelSim работать не будет, поэтому будем использовать дополнительную команду reset_count, которая инициализирует счетчик, обнуляя его, т.е.


module resCount (reset_count, counter, timer555, 
                         RAM_button, data_in, RAM_out);
   parameter ADDR_WIDTH = 4;
   parameter DATA_WIDTH = 8;

  input reset_count;
  output [ADDR_WIDTH-1:0] counter;
  input timer555;
  input RAM_button;
  input [DATA_WIDTH-1:0] data_in;
  output [DATA_WIDTH-1:0] RAM_out;
wire Counter_load;
assign Counter_load = RAM_out[7];
reg [ADDR_WIDTH-1:0] counter;
always @ (posedge timer555 or posedge reset_count)
  if (reset_count)
        counter <= 4'b0000;  
  else if (Counter_load) 
        counter <= RAM_out[3:0];  
  else
        counter <= counter + 4'b0001;
 wire [ADDR_WIDTH-1:0] adr;
    assign adr = counter;
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM_button) 
        mem [adr] <= data_in;
assign RAM_out = mem[adr]; 
endmodule

test bench


module tresCount;
   parameter ADDR_WIDTH = 4;
   parameter DATA_WIDTH = 8;

   reg reset_count; 
   reg timer555, RAM_button;
   wire [ADDR_WIDTH-1:0] counter;
   reg [DATA_WIDTH-1:0] data_in;
   wire [DATA_WIDTH-1:0] RAM_out;
resCount test_resCount(reset_count, counter, 
                                 timer555, RAM_button, data_in, RAM_out);
initial // Clock generator
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end
initial 
  begin
    data_in[0] = 0;
    data_in[1] = 0;
    data_in[2] = 0;
    data_in[3] = 0;
    data_in[4] = 0;
    data_in[5] = 0;
    data_in[6] = 0;
    data_in[7] = 0;
    RAM_button = 0;
    reset_count =1;
    #5 reset_count =0;
    #1500 data_in[7] =1;
    #5 RAM_button = 1;
    #5 data_in[7] =0; RAM_button = 0;
  end
endmodule    


Добавим в схему MUX2 и Асс. Будем производить запись в Асс командой RAM_out[6].


assign Acc_button = RAM_out[6];

К тактовому входу Асс подключим лог. элемент И


//в модуле regiser4 заменим (posedge reg_button) на (negedge reg_button)
.reg_button(Acc_button & timer555),

Смысл подключения лог. элемента И к тактовому входу в том, что теперь по фронту timer555 можно переключать мультиплексор, а по спаду производить запись в аккумулятор. Т.о. мы поместили две команды в один такт.


Будем производить переключение MUX2 командой RAM_out[5]


assign MUX_switch = RAM_out[5];


module register4
(
  input  [3:0] reg_data,
  input reg_button,
  output reg [3:0] q  
);
always @(negedge reg_button) // заменим "posedge" на  "negedge"
         q <= reg_data;
endmodule

module R50 (reset_count, counter, timer555, RAM_button, data_in, 
                  RAM_out, mux_switch_out, mux_out,Acc_out);
   parameter ADDR_WIDTH = 2;
   parameter DATA_WIDTH = 8;

  input reset_count;
  output [ADDR_WIDTH-1:0] counter;
  input timer555;
  input RAM_button;
  input [DATA_WIDTH-1:0] data_in;
  output [DATA_WIDTH-1:0] RAM_out;
  output [3:0] Acc_out;

  output mux_switch_out;
  output [3:0] mux_out;
wire Counter_load;
assign Counter_load = RAM_out[7];
//Counter
reg [ADDR_WIDTH-1:0] counter;
always @ (posedge timer555 or posedge reset_count)
  if (reset_count)
        counter <= 2'b00;  
  else if (Counter_load) 
        counter <= RAM_out[1:0];  
  else
        counter <= counter + 2'b01;

wire [ADDR_WIDTH-1:0] adr;
 assign adr = counter;
//RAM
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM_button) 
        mem [adr] <= data_in;
assign RAM_out = mem[adr]; 
// MUX2
wire MUX_switch;
assign MUX_switch = RAM_out[5];
reg [3:0] MUX2; 
always @*
MUX2 = MUX_switch ? RAM_out : data_in[3:0]; // возьмём 4 разряда из data_in
assign mux_out = MUX2;
assign mux_switch_out = MUX_switch;

wire Acc_button;
assign Acc_button = RAM_out[6];
//Acc
register4 Acc_reg(
    .reg_data(mux_out),
    .reg_button(Acc_button & timer555),
    .q(Acc_out)
);
endmodule

В тестбенче запишем в ячейку 00 число 0101, а в ячейку 01 число 1010; загрузим эти числа в аккумулятор


module tR50;
   parameter ADDR_WIDTH = 2;
   parameter DATA_WIDTH = 8;

   reg reset_count; 
   reg timer555, RAM_button;
   wire [ADDR_WIDTH-1:0] counter;
   reg [DATA_WIDTH-1:0] data_in;
   wire [DATA_WIDTH-1:0] RAM_out;

   wire mux_switch_out;
   wire [3:0] mux_out;
   wire [3:0] Acc_out;
R50 test_R50(reset_count, counter, timer555, RAM_button, data_in, 
                  RAM_out, mux_switch_out, mux_out,Acc_out);
initial // Clock generator
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end
initial 
  begin
  data_in[0] = 1;
  data_in[1] = 0;
  data_in[2] = 1;
  data_in[3] = 0;
  data_in[4] = 0;
  data_in[5] = 1;
  data_in[6] = 1;
  data_in[7] = 0;
  RAM_button = 0;
  reset_count =1;  
  #5 RAM_button = 1; reset_count = 0; 
  #5 data_in[0]=0; data_in[2]=0; data_in[5]=0; data_in[6]=0; RAM_button=0;
  #15 data_in[1]=1; data_in[3]=1; data_in[5]=1;data_in[6]=1;
  #5 RAM_button=1; 
  #5 data_in[1]=0; data_in[3]=0; data_in[5]=0; data_in[6]=0; RAM_button=0; 
  end
endmodule    


Поместим второе ОЗУ в общую схему и будем производить запись в ОЗУ командой RAM1_out[4].


assign RAM2_button = RAM1_out[4];


module register4
(
  input  [3:0] reg_data,
  input reg_button,
  output reg [3:0] q  
);
always @(negedge reg_button) 
         q <= reg_data;
endmodule

module R51 (reset_count, counter, timer555, RAM1_button, data_in, 
            RAM1_out, RAM2_out, mux_switch_out, mux_out,Acc_out);
   parameter ADDR_WIDTH = 3;
   parameter DATA_WIDTH = 8;

  input reset_count;
  output [ADDR_WIDTH-1:0] counter;
  input timer555;
  input RAM1_button;
  input [DATA_WIDTH-1:0] data_in;
  output [DATA_WIDTH-1:0] RAM1_out;
  output [3:0] RAM2_out;
  output [3:0] Acc_out;

  output mux_switch_out;
  output [3:0] mux_out;
wire Counter_load;
assign Counter_load = RAM1_out[7];
//Counter
reg [ADDR_WIDTH-1:0] counter;
always @ (posedge timer555 or posedge reset_count)
  if (reset_count)
        counter <= 2'b00;  
  else if (Counter_load) 
        counter <= RAM1_out[1:0];  
  else
        counter <= counter + 2'b01;

wire [ADDR_WIDTH-1:0] adr1;
 assign adr1 = counter;
//RAM1
reg [DATA_WIDTH-1:0] mem1 [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM1_button ) 
        mem1 [adr1] <= data_in;
assign RAM1_out = mem1[adr1]; 

wire [ADDR_WIDTH-1:0] adr2;
 assign adr2 = RAM1_out[3:0];
wire RAM2_button;
   assign RAM2_button = RAM1_out[4];
//RAM2
reg [3:0] mem2 [2**ADDR_WIDTH-1:0]; 
always @(posedge RAM2_button)
   mem2 [adr2] <= Acc_out;
assign RAM2_out = mem2[adr2]; 
// MUX2
wire MUX_switch;
 assign MUX_switch = RAM1_out[5];
reg [3:0] MUX2; 
always @*
MUX2 = MUX_switch ? RAM2_out : data_in[3:0];
assign mux_out = MUX2;
assign mux_switch_out = MUX_switch;

wire Acc_button;
assign Acc_button = RAM1_out[6];
//Acc
register4 Acc_reg(
   .reg_data(mux_out),
   .reg_button(Acc_button & timer555),
   .q(Acc_out)
);
endmodule

В тестбенче загрузим числа 0100 и 1000 из Асс в нулевую 0000 и первую 0001 ячейки ОЗУ mem2 (затем загрузим эти числа в Асс из ОЗУ mem2)


module tR51;
   parameter ADDR_WIDTH = 3;
   parameter DATA_WIDTH = 8;

   reg reset_count; 
   reg timer555, RAM1_button;
   wire [ADDR_WIDTH-1:0] counter;
   reg [DATA_WIDTH-1:0] data_in;
   wire [DATA_WIDTH-1:0] RAM1_out;
   wire [3:0] RAM2_out;

   wire mux_switch_out;
   wire [3:0] mux_out;
   wire [3:0] Acc_out;
R51 test_R51(reset_count, counter, timer555, RAM1_button, data_in, 
             RAM1_out, RAM2_out, mux_switch_out, mux_out,Acc_out);
initial // Clock generator
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end
initial 
  begin
  data_in[0] = 0;
  data_in[1] = 0;
  data_in[2] = 0;
  data_in[3] = 0;
  data_in[4] = 0;
  data_in[5] = 0;
  data_in[6] = 1;
  data_in[7] = 0;
  RAM1_button = 0;
  reset_count =1;  
  #5 RAM1_button = 1; reset_count = 0;  
  #5 RAM1_button = 0; data_in[6] = 0;

  #10 data_in[4] = 1; 
  #5 RAM1_button = 1;
  #5 data_in[4] = 0; RAM1_button = 0;

  #30 data_in[6] = 1; 
  #5 RAM1_button = 1; 
  #5 data_in[6] = 0; RAM1_button = 0;

  #30  data_in[4] = 1;     data_in[0] = 1; 
  #5 RAM1_button = 1;
  #5 data_in[4] = 0; data_in[0] = 0; RAM1_button = 0;

  #30 data_in[6] = 1; 
  #5 RAM1_button = 1;
  #5  RAM1_button = 0; data_in[6] = 0;

  #30 data_in[5] = 1; data_in[6] = 1;
  #5 RAM1_button = 1;
  #5  RAM1_button = 0; data_in[5] = 0; data_in[6] = 0;

  #30 data_in[5] = 1; data_in[6] = 1; data_in[0] = 1;
  #5 RAM1_button = 1;
  #5 RAM1_button = 0; data_in[0] = 0; data_in[5] = 0; data_in[6] = 0;

  #70 data_in[2] = 1;
  #80 data_in[2] = 0; data_in[3] = 1;
  #40 data_in[3] = 0;
  end
endmodule    


Добавлю, что схема c лог. элементом И на тактовом входе аккумулятора не всегда будет работать корректно (зависит от платы). Заменим лог. элемент И на триггер Acc_dff, загрузку в триггер будем производить по отрицательному фронту (по спаду) тактового сигнала timer555, загрузку в аккумулятор будем производить по положительному фронту


// Acc_dff
reg Acc_dff;
always @(negedge timer555)
        Acc_dff <= Acc_button;  

Итак, добавив остальные команды, создадим модуль R52 (LMC)



module register4
(
  input  [3:0] reg_data,
  input reg_button,
  output reg [3:0] q  
);
always @(posedge reg_button) // negedge -> posedge
         q <= reg_data;
endmodule

module R52 (Z_flag, PZ_flag, reset_count, counter, timer555, RAM1_button, data_in, 
           RAM1_out, RAM2_out, mux_switch_out, mux_out, Acc_out, data_out, Acc_dff);
  parameter ADDR_WIDTH = 4;
  parameter DATA_WIDTH = 12;

  input reset_count;
  input timer555;
  input RAM1_button;
  input [DATA_WIDTH-1:0] data_in;

  output [ADDR_WIDTH-1:0] counter;
  output [1:0] mux_switch_out;
  output [3:0] mux_out;
  output [3:0] Acc_out;
  output [3:0] data_out;
  output [DATA_WIDTH-1:0] RAM1_out;
  output [3:0] RAM2_out;
  output Z_flag, PZ_flag;
  output Acc_dff;

wire JMP_button, Z_JMP_button,PZ_JMP_button;
  assign JMP_button = RAM1_out[6];
  assign Z_JMP_button = RAM1_out[5];
  assign PZ_JMP_button = RAM1_out[4];   

wire Z_JMP,PZ_JMP;
 assign Z_JMP = Z_flag & Z_JMP_button;
 assign PZ_JMP = PZ_flag & PZ_JMP_button;   

//Counter
reg [ADDR_WIDTH-1:0] counter;
 always @ (posedge timer555 or posedge reset_count)
  if (reset_count)
        counter <= 4'b0000;  
  else if (JMP_button|Z_JMP|PZ_JMP)
        counter <= RAM1_out[3:0];  
  else
        counter <= counter + 4'b0001;

wire [ADDR_WIDTH-1:0] adr1;
 assign adr1 = counter;
//RAM1
reg [DATA_WIDTH-1:0] mem1 [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM1_button ) 
        mem1 [adr1] <= data_in;
 assign RAM1_out = mem1[adr1]; 
//RAM2_adr
wire [ADDR_WIDTH-1:0] adr2;
   assign adr2 = RAM1_out[2:0];
//RAM2_button
wire RAM2_button;
   assign RAM2_button = RAM1_out[11];
//RAM2  
reg [3:0] mem2 [2**ADDR_WIDTH-1:0]; 
    always @(posedge RAM2_button) 
         mem2 [adr2] <= Acc_out;
 assign RAM2_out = mem2[adr2];        
// sum 
wire [3:0] sum;
 assign sum =  Acc_out + RAM2_out;
//subtract
wire [3:0] subtract;
 assign subtract =  Acc_out - RAM2_out;
// MUX4
wire [1:0] mux_switch;
 assign mux_switch[0] = RAM1_out[7];
 assign mux_switch[1] = RAM1_out[8];
reg [3:0] MUX4; 
always @*
MUX4 = mux_switch[1] ? (mux_switch[0] ? RAM2_out : subtract)
: (mux_switch[0] ? sum : data_in[3:0]);

 assign mux_out = MUX4;
 assign mux_switch_out[0] = mux_switch[0];
 assign mux_switch_out[1] = mux_switch[1];
//Acc_button
wire Acc_button;
 assign Acc_button = RAM1_out[10];
// Acc_dff
reg Acc_dff;
always @(negedge timer555)
   Acc_dff <= Acc_button;  
//Acc
register4 Acc_reg(
    .reg_data(mux_out),
     //.reg_button(Acc_button & timer555),
    .reg_button(Acc_dff),
    .q(Acc_out)
);
//data_out
wire Output_button;
 assign Output_button = RAM1_out[9];
register4 Output_reg(
    .reg_data(Acc_out),
    .reg_button(Output_button),
    .q(data_out)
);
// flags
 assign Z_flag =  ~(|Acc_out);
 assign PZ_flag =  ~Acc_out[3];
endmodule

В тестбенче проверим, как работает алгоритм поиска максимального числа.


Особенность загрузки команд в ОЗУ заключается в том, что после загрузки всех команд нам приходится возвращаться (340ns) в ячейку 8 и загружать ещё одну команду


module tR52;
   parameter ADDR_WIDTH = 4;
   parameter DATA_WIDTH = 12;

  reg reset_count;
  reg timer555;
  reg RAM1_button;
  reg [DATA_WIDTH-1:0] data_in;

  wire [ADDR_WIDTH-1:0] counter;
  wire [1:0]mux_switch_out;
  wire [3:0] mux_out;
  wire [3:0] Acc_out;
  wire [3:0] data_out;
  wire [DATA_WIDTH-1:0] RAM1_out;
  wire [3:0] RAM2_out;
  wire Z_flag, PZ_flag;
  wire Acc_dff;

R52 test_R52(Z_flag, PZ_flag, reset_count, counter, timer555, RAM1_button, data_in,
    RAM1_out, RAM2_out, mux_switch_out, mux_out,Acc_out, data_out, Acc_dff);
initial // Clock generator
  begin
    timer555 = 0;
    forever #20 timer555 = ~timer555;
  end

initial 
  begin
  data_in[0] = 0;
  data_in[1] = 0;
  data_in[2] = 0;
  data_in[3] = 0;
  data_in[4] = 0;
  data_in[5] = 0;
  data_in[6] = 0;
  data_in[7] = 0;
  data_in[8] = 0;
  data_in[9] = 0;
  data_in[10] = 1;
  data_in[11] = 0;
  RAM1_button = 0;
  reset_count =1;  
  // загружаем 1-ое число в Асс
  #5 RAM1_button = 1; reset_count = 0;  
  #5 RAM1_button = 0; data_in[10] = 0; data_in[0] = 0;
  // сохраняем 1-ое число в ячейке 0
  #10 data_in[11] = 1; 
  #5 RAM1_button = 1;
  #5 data_in[11] = 0; RAM1_button = 0;
  // загружаем 2-ое число в Асс
  #30 data_in[10] = 1;  
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[10] = 0;  
   // сохраняем 2-ое число в ячейке 0
  #30 data_in[11] = 1;data_in[0] = 1; 
  #5 RAM1_button = 1;
  #5 data_in[11] = 0;data_in[0] = 0; RAM1_button = 0;
   //вычитаем 1-ое число из Асс
  #30 data_in[8]=1; data_in[10] = 1;  
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0;  data_in[8]=0; data_in[10] = 0;  
   // Если Acc>=0, переходим на ячейку 8
  #30 data_in[4]=1;   data_in[3]=1;  
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0;  data_in[4]=0; data_in[3]=0; 
  // загружаем 1-ое число
  #30 data_in[7] = 1; data_in[8] = 1;   data_in[10] = 1;  
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[7] = 0; data_in[8] = 0; data_in[10] = 0;  
  // безусловный переход в ячейку 9
  #30 data_in[6] = 1; data_in[3]=1; data_in[0]=1;   
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[6] = 0;  data_in[3]=0; data_in[0]=0;  
   //выводим число в data_out
  #30 data_in[9] = 1;
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[9] = 0;  
  // безусловный переход в ячейку 8
  #30 data_in[6] = 1; data_in[3]=1; data_in[0]=0;  
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[6] = 0;  data_in[3]=0; data_in[0]=0;  
  //загружаем 2-ое число
  #30 data_in[7] = 1; data_in[8] = 1;   data_in[10] = 1; data_in[0] = 1; 
  #5 RAM1_button = 1; 
  #5 RAM1_button = 0; data_in[7] = 0; data_in[8] = 0; data_in[10] = 0; data_in[0] = 0;

  #75 RAM1_button = 1;
  #5 RAM1_button = 0;

  #230 data_in[2]=1;  data_in[0]=0; //первое число
  #80 data_in[2]=0; data_in[0]=1; // второе число
 end
endmodule    

Ссылка на github с кодами программ.


Бесплатную студенческую версию ModelSim под Windows можно скачать с сайта www.model.com.
Далее необходимо (заполнив форму) скачать файл student_license.dat и поместить этот файл в основную директорию программы ModelSim.


Ссылка на файл ModelSim под Linux (Ubuntu) здесь
Инструкция по установке здесь.

Проектирование процессора Verilog

Reading time6 min
Views23K

Часть I
Часть II
Часть III
Часть IV


Спроектируем Little Man Computer на языке Verilog.


Статья про LMC была на Хабре.


Online симулятор этого компьютера здесь.


Напишем модуль оперативной памяти RAM/ОЗУ, состоящий из четырех (N=2) четырёхбитных (M=4) слов. Данные загружаются в ОЗУ из data_in по адресу adr при нажатии на кнопку:


module R0 #(parameter N = 2, M = 4)
(
input RAM_button, //кнопка
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]; //объявляем массив mem
always @(posedge RAM_button) //при нажатии на кнопку
mem [adr] <= data_in; //загружаем данные в ОЗУ из data_in 
assign RAM_out = mem[adr]; //назначаем RAM_out портом вывода данных
endmodule
```<cut/>
В качестве внешнего генератора подключим КМОП <b>таймер 555</b> (работающий от 3.3V).
Подключим <b>таймер 555</b> к счётчику, подключим счётчик к адресному входу <b>ОЗУ</b>:
```verilog
module R1 #(parameter N = 2, M = 4)
(
input timer555, RAM_button,
//input [N-1:0] adr,
input [M-1:0] data_in,
output [M-1:0] RAM_out
);
reg [1:0]counter; //объявляем счётчик
always @(posedge timer555) //при поступлении тактового сигнала
 counter <= counter + 1;  // счетчик увеличивается на 1
 wire [N-1:0] adr;
 assign adr = counter; // подключаем счётчик на адресный вход ОЗУ
reg [M-1:0] mem [2**N-1:0];
always @(posedge RAM_button)
 mem [adr] <= data_in;
assign RAM_out = mem[adr];
endmodule

Здесь при описании счетчика counter и памяти mem используются неблокирующие присвоения <= Операторы присвоения рассматриваются на сайте marsohod.org здесь
Описание работы счетчика есть на сайте marsohod.org здесь


Добавим в счетчик функцию загрузки.
Загрузка осуществляется командой Counter_load:


//input Counter_load; 
wire [3:0] branch_adr; // адрес перехода
assign branch_adr = data_in; 
always @(posedge timer555)
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_button,
  output reg [3:0] q  
);
always @(posedge reg_button)
         q <= reg_data;
endmodule

Добавим в общую схему аккумулятор Acc, мультиплексор MUX2 и сумматор sum.
Сумматор прибавляет к числу в аккумуляторе Acc числа из памяти.
На сигнальные входы мультиплексора подаются числа data_in и sum.
Далее число из мультиплексора MUX2 загружается в аккумулятор Acc:


module R2 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4)
(
input timer555, Counter_load, RAM_button,
input MUX_switch,
input Acc_button, 
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 timer555)
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_button)
mem [adr] <= Acc;
assign RAM = mem[adr];
//sum
wire [3:0] sum;
assign sum =  Acc + RAM;
//MUX
reg [3:0] MUX2; 
always @*  // Always @* — значит «всегда» 
MUX2 = MUX_switch ? sum : data_in;
//Accumulator
register4 Acc_reg(
.reg_data(MUX2),
.reg_button(Acc_button),
.q(Acc)
);
endmodule

Always @ — значит «всегда». Некоторые синтезаторы не понимают эту конструкцию. Мультиплексор можно написать и без Always @ (тут используется просто для примера).



Вычитание


Для того, чтобы произвести вычитание, надо представить вычитаемое число в дополнительном коде. Про сложение и вычитание двоичных чисел можно прочитать в учебнике "Цифорвая схемотехника и архитектура компьютера" (Дэвид М. Харрис и Сара Л. Харрис) в главе 1.4.6 Знак двоичных чисел


Добавим в основной модуль элемент, вычитающий из числа в аккумуляторе числа, хранящиеся в памяти:


wire [3:0] subtract;
assign subract =  Acc - RAM ;

Заменим 2-входовой мультиплексор 4-входовым:


always @*
MUX4 = MUX_switch[1] ? (MUX_switch[0] ? RAM : subtract)
: (MUX_switch[0] ? sum : data_in);

Подключим к аккумулятору устройство вывода (4bit'ный регистр), также подключим к аккумулятору 2 флага:


  1. Флаг "Ноль" — это лог. элемент 4ИЛИ-НЕ. Флаг поднимается, если содержимое Асс равно нулю.


  2. Флаг "Ноль или Положительное число" — это лог. элемент НЕ на старшем разряде 4-разрядного аккумулятора. Флаг поднимается, если содержимое Асс больше или равно нулю.



//флаг "Ноль" 
output Z_flag;
assign Z_flag =  ~(|Acc); // 4-входовой вентиль ИЛИ-НЕ
//флаг "Ноль или Положительное число"
output PZ_flag;
assign PZ_flag =  ~Acc[3]; 

4ИЛИ-НЕ

Здесь мы описали многовходовой вентиль ИЛИ-НЕ как ~(|Acc)
Также в языке Verilog поддерживается набор типов логических вентилей (Gate Types).


Для логических вентилей определены ключевые слова: and (И), nand (И-НЕ), or (ИЛИ), nor (ИЛИ-НЕ), xor (Исключающее ИЛИ), xnor (Исключающее ИЛИ-НЕ), buf (Буферный элемент), not (Отрицание, НЕ).


В Verilog при использовании вентилей необходимо задать входы и выходы элемента, а также (не обязательно) имя вентиля. Например, вентили and и or должны иметь один выход и два и более входов. Так, для вентиля nor имеем
nor name listof arguments
nor mynor(out, in0, in1, in2, in3);



Добавим три команды


  1. загрузка содержимого аккумулятора в устройство вывода data_out
  2. загрузка адреса в счётчик, если поднят флаг "ноль" (JMP if Acc=0)
  3. загрузка адреса в счётчик, если поднят флаг "ноль или положительное число" (JMP if Acc>=0)

module R3 #(parameter ADDR_WIDTH = 2, DATA_WIDTH = 4)
(
input timer555, RAM_button,
input JMP, Z_JMP, PZ_JMP,
input [1:0] MUX_switch,
input Acc_button, 
input Output_button,
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 timer555)
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_button)
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_button),
.q(Acc)
);
register4 Output_reg(
.reg_data(Acc),
.reg_clk(Output_button),
.q(data_out)
);
assign Z_flag =  ~(|Acc);
assign PZ_flag =  ~Acc[3]; 
endmodule


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



Схему можно скачать отсюда.


В первых восьми разрядах хранятся команды, в последних четырех разрядах хранится адрес, загружаемый в счётчик.


Вообще, загрузка числа в аккумулятор Асс должна производиться после переключения мультиплексора 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/


Алгоритм работает так: сохраняем в память данных два числа из data_in. Вычитаем из второго числа первое:


  • если результат отрицательный, записываем первое число в Асс, записываем в data_out число из Асс;
  • если результат положительный, записываем второе число в Асс, записываем в data_out число из Асс.

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



Элемент НЕ на управляющем входе счётчика, необходимый для загрузки данных в счётчик — это такая особенность программы Logisim, в реальных схемах элемент НЕ на управляющем входе не требуется (по крайней мере я таких счётчиков не знаю).


Quartus II можно скачать с официального сайта.


При регистрации в разделе My Primary Job Function is* необходимо выбрать пункт Student.
Далее необходимо скачать драйвер для программатора (драйвер для usb-blaster'a можно установить из C:\altera...\quartus\drivers\usb-blaster).


Logisim можно скачать здесь.

Проектирование процессора (CPU Design) [First ver.]

Reading time5 min
Views23K
Это начальный вариант статьи о процессоре, конечный вариант этой же статьи здесь.

Спроектируем в Logisim'е устройство, позволяющие суммировать наборы чисел, хранящихся в памяти. Возьмем набор восьмиразрядных чисел и подключим его к мультиплексору, переход от одного числа к другому будем осуществлять с помощью счетчика, подключенного к выбирающему входу мультиплексора, а к выходу мультиплексора подключим сумматор и аккумулятор. В качестве тактового генератора будем использовать кнопку. Данные будут загружаться в аккумулятор при отпускании кнопки (это осуществляется с помощью элемента НЕ, подключенного к кнопке).


Проектирование процессора Logisim

Reading time4 min
Views73K

Часть I
Часть II
Часть III
Часть IV


Одна из глав книги "Код" Чарльза Петцольда посвящена проектированию блоков CPU и в начале главы описывается устройство, позволяющие суммировать наборы чисел, хранящихся в памяти. Спроектируем похожую схему в Logisim. Возьмем набор восьмиразрядных чисел и подключим его к мультиплексору, переход от одного числа к другому будем осуществлять с помощью счетчика, подключенного к выбирающему входу мультиплексора, а к выходу мультиплексора подключим сумматор и аккумулятор. В качестве тактового генератора будем использовать кнопку. Данные будут загружаться в аккумулятор при отпускании кнопки. Это осуществляется с помощью элемента НЕ, подключенного к кнопке. Про реализацию этих функциональных блоков в виде отдельных микросхем далее в статье.


Читать дальше →

Игры на Scheme(Lisp) в среде DrRacket

Reading time4 min
Views7.8K
Для запуска программ, приведённых в статье, можно использовать DrRacket. Для начала рассмотрим связь конечного автомата и игрового процесса. Объект управления в игре можно представить в виде конечного автомата. Рассмотрим программу, моделирующую светофор. Этот пример был описан в предыдущей статье.

Переходом в другое устойчивое состояние является переключение сигнала светофора. Диаграмму состояний можно изобразить в следующем виде.

image
Читать дальше →

Как проектировать программы (HtDP)

Reading time6 min
Views18K
Следующая статья о том, как писать игры на Scheme

Учебник HtDP (How to Design Programs), посвящен программированию на языке Scheme в среде drRacket.
drRacket можно скачать с сайта.
Вводная часть учебника содержит описание функции empty-scene, предназначенной для работы с изображениями. Например, эта программа создает пустую сцену

#lang racket  
(require 2htdp/image)      ;библиотека для работы с изображениями 
(empty-scene 100 60)     ;сцена (канвас) размером 100х60 

Читать дальше →

MASM, TASM, FASM, NASM под Windows и Linux

Reading time5 min
Views178K
В данной статье я хочу рассмотреть вопросы, которые могут возникнуть у человека, приступившего к изучению ассемблера, связанные с установкой различных трансляторов и трансляцией программ под Windows и Linux, а также указать ссылки на ресурсы и книги, посвященные изучению данной темы.

MASM


Используется для создания драйверов под Windows.
Читать дальше →

Information

Rating
11,649-th
Registered
Activity