
Сообщение от
Vslav
Можно представить как объект класса с внутренним состоянием.
Но так и задумывалось изначально. Каждый модуль - самостоятельный объект класса, с переменными, в которых хранится его внутреннее состояние.

Сообщение от
Vslav
А также должны щелкать все триггеры во всей системе одновременно.
Это условие автоматически соблюдается при выполнении функции eval, внутри которой вызываются другие функции eval объектов класса (модулей, которые вызываются внутри другого модуля).

Сообщение от
Vslav
Если сначала вызвать таймер - он инкрементируется, и при вызове обработки регистра-защетки чтения, будет защелкнуто неверное значение.
А вот как раз это я и пытаюсь уже 3 поста уяснить. Когда именно нужно делать вызов, и что должно быть входом. Поскольку порядок исполнения assignов и alwaysов не важен, то исполняться они будут в том порядке, в каком встречаются по тексту, и тут-то и возник вопрос, куда втыкать вызов eval подчинённых классов. Но это я кажется начинаю уже понимать.

Сообщение от
Vslav
Следует взять ветку Qsync
Эта ветка принципиально не конвертируется, т.к. не хватает модулей. В файле vm1_alib.v есть вызов модулей altsyncram и altpll, которых нету, если нету квартуса или модельсима.
Вот что получилось для ir13. Файл ir13.h
Код:
class ir13
{
//input-output
BOOL rst_n, // input rst_n, // async reset, active low
BOOL clk, // input clk, // sync clock, raising edge
BYTE s, // input [2:1] s, // shift mode [1:0]
BYTE d, // input [7:0] d, // parallel load data
BOOL dsr, dsl; // input dsr, dsl; // new shifted in data bit
BYTE q // output reg [7:0] q // output data
//внутренние переменные
BYTE q_rc; //wire [7:0] q_rc;
BOOL clk_prev; //предыдущее значение
//отлов фронта
//вход: sc - текущее состояние сигнала,
// sp - предыдущее состояние сигнала
inline BOOL posedge(BOOL sc, BOOL sp)
{
//выход - когда предыдущее состояние FALSE, а текущее - TRUE
return !sp && sc;
}
//отлов спада
//вход: sc - текущее состояние сигнала,
// sp - предыдущее состояние сигнала
inline BOOL negedge(BOOL sc, BOOL sp)
{
//выход - когда предыдущее состояние TRUE, а текущее - FALSE
return sp && !sc;
}
void assign1();
public:
ir13(void);
~ir13(void){};
void eval(BOOL in_rst_n, BOOL in_clk, BYTE in_s, BYTE in_d, BOOL in_dsr, BOOL in_dsl, BYTE &out_q);
};
файл ir13.cpp
Код:
#include "ir13.h"
ir13::ir13(void)
: rst_n(FALSE), clk(FALSE), dsr(FALSE), dsl(FALSE), s(0), d(0), q(0)
{
clk_prev = clk;
}
void ir13::assign1()
{
switch (s & 3)
{
case 0: // storage
q_rc = q;
break;
case 1: // shift left
q_rc = (q >> 1) | (dsl ? 0x80 : 0);
break;
case 2: // shift right
q_rc = (q << 1) | (dsr ? 1 : 0);
break;
case 3: // parallel load
q_rc = d;
break;
}
}
void ir13::eval(BOOL in_rst_n, BOOL in_clk, BYTE in_s, BYTE in_d, BOOL in_dsr, BOOL in_dsl, BYTE &out_q)
{
//назначаем переданные входные переменные, чтобы если много assignов,
//то все они прозрачно использовали внутренние переменные.
rst_n = in_rst_n;
clk = in_clk;
s = in_s;
d = in_d;
dsr = in_dsr;
dsl = in_dsl;
//отрабатываем все операторы assign
//в данном конкретном случае, тело функции assign1 можно перенести сюда
//и все эти переменные, которые выше - будут не нужны.
assign1();
//делаем снапшот текущего состояния внутренних переменных для блоков always - нет необходимости
//выполняем все always
if (!rst_n)
{
q = 0;
}
else if (posedge(clk, clk_prev))
{
q = q_rc;
}
//снова обрабатываем assing (переменные же сменились)
assign1();
//возвращаем выходные переменные
out_q = q;
clk_prev = clk;
};
и примерно как это предполагается использовать:
Код:
#include "ir13.h"
....
ir13 m_regIR13_1; //объект регистра ИР13
//переменные для взаимодействия с объектом m_regIR13_1
//они могут принимать значения при взаимодействии с пользовательским UI например.
BOOL m_bSig_Reset; // async reset, active low
BOOL m_bSigClock; // sync clock, raising edge
int m_nFreq; //частота. меняется в диапазоне 1..1000
int m_nShiftMode; //shift mode [1:0]
BYTE m_nData; // parallel load data
BOOL m_nDSR, m_nDSL; // new shifted in data bit
BYTE m_nQ; // output data
....
//рабочий цикл
while (1)
{
//отрабатываем передний фронт и первый полупериод интервала ТЧ
m_bSigClock = TRUE;
//вызываем функцию объекта для переднего фронта ТЧ
m_regIR13_1.eval(m_bSig_Reset, m_bSigClock, LOBYTE(m_nShiftMode), m_nData, m_nDSR, m_nDSL, m_nQ);
//отрабатываем задний фронт и второй полупериод интервала ТЧ
m_bSigClock = FALSE;
//вызываем функцию объекта для заднего фронта ТЧ
m_regIR13_1.eval(m_bSig_Reset, m_bSigClock, LOBYTE(m_nShiftMode), m_nData, m_nDSR, m_nDSL, m_nQ);
sleep(1000/m_nFreq); //вот поэтому и в диапазоне 1..1000
}
....
Я не стал расписывать назначения побитно, т.к. это было бы удобно если бы d, q и q_rc представлялись бы в виде массивов BOOL q[8]. Но такое представление становится неудобным для логики редуцирования и интерпретации значения как числа.