User Tag List

Страница 54 из 273 ПерваяПервая ... 505152535455565758 ... ПоследняяПоследняя
Показано с 531 по 540 из 2727

Тема: Цифровая археология: 1801 и все-все-все

  1. #531

    Регистрация
    31.03.2013
    Адрес
    г. Киев
    Сообщений
    2,413
    Спасибо Благодарностей отдано 
    132
    Спасибо Благодарностей получено 
    759
    Поблагодарили
    353 сообщений
    Mentioned
    88 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Увы, я не очень хороший объясняльщик. Кроме того, хотя я также являюсь и программистом, мне тоже приходится по ходу дискуссии обдумывать как это все лучше реализовать программно. Так что польза от обсуждения обоюдная
    Цитата Сообщение от gid Посмотреть сообщение
    т.е. следуя этой логике, vm1_pli pli_matrix(.rq(rq), .sp(pli)); нужно внутри модуля vm1_qbus развернуть как include и все его assignы выполнять как assignы модуля vm1_qbus и не вызывать его как внешнюю функцию.
    Да, по сущности это инклуд/макрос. Включаются внутренние блоки модуля. Но, синтаксически, сами сигналы можно рассматривать не как переменные, а как функции. Типа:
    Код:
    plm_ena_fc_var = plm_ena_fc(....);
    Тогда очень уж сильного разворачивания можно избежать и использовать много готового кода из имеющегося верилога. И часть модулей можно будет представить как функции. Вот vm1_pli и vm1_plm содержат только assign - их можно представить как фукнции. vm1_timer - уже имеет внутри триггеры, его как функцию не представить.

    Цитата Сообщение от gid Посмотреть сообщение
    Поэтому, если с assignами и alwaysами как-то более-менее понятно, то с вызовом модуля из другого модуля, если его рассматривать именно как внешнюю функцию, возникает неясность.
    Нет, Вы правильно сказали, модуль надо рассматривать как include или макрос, в том смысле что его внутренние блоки включаются в проект и внутренний текст разворачивается - все внутренние assign и always включаются в текущий уровень вложений. То есть модуль - это НЕ вызов функции/функций в общем случае. Отдельный assign - да, можно рассмотреть как вызов программной функции. Кстати, в Верилоге есть свои таски и функции, хорошо что я их тут не применял

    Цитата Сообщение от gid Посмотреть сообщение
    С моей точки зрения это наоборот усложнение. Если always сложный и развесистый, то количество используемых переменных может возрасти кратно
    always не может быть сложным и развеститым, его суть - указать триггеры которые назначаются по событию клока. То, что там внутри понаписаны какие-то функции - это просто синтаксическое удобство, внос неявного assigna. Я просто предложил сделать их явными. Тогда в always вообще не придется ничего вычислять, просто тупо переписать в триггеры по событию клока результаты из массива вычисленных значений функций. Никаких снапшотов и раздумий что и откуда брать.

    Цитата Сообщение от gid Посмотреть сообщение
    может не получиться выстроить все assignы в порядке, когда не нужно их крутить в цикле.
    Получится, абсолютно весь проект сводится к парам функция/триггер. И все функции имеют в качестве аргументов внешние входные значения (для модуля процессора это внешние пины) и триггеры. Если входным значением вдруг является значение другой функции - то эта другая все равно в итоге доходит до внешних параметров и триггеров. То есть любая функция в проекте зависит только от внешних сигналов и состояния триггеров. Все функции-аргменты являются промежуточными и в итоге тоже приходят к зависимости только от внешних сигналов и триггеров.

    Эту промежуточную функцию-аргумент можно или вычислить ее значение и "закешировать" для использования в других выражениях (как аргумент других функций) - то есть назначить результат какой-то переменной, или для начала, для простоты, тупо вычислять каждый раз - тогда не нужен цикл, все результаты будут актуальными сразу.

    Цитата Сообщение от gid Посмотреть сообщение
    А выход из цикла - сравнение предыдущего состояния списка переменных с новым, там одно это сравнение будет сколько ресурсов отжирать.
    Да, цикл он только для иллюстрации был, как метод вычисления, независящий от порядка операторов. Не лучшая идея, согласен.

    ---------- Post added at 18:26 ---------- Previous post was at 17:51 ----------

    Давайте вместе подумаем на совсем простом примере, я вот набросал 155ИР13, синхронный сдвиговый регистр, с параллельной загрузкой и асинхронным сбросом, чтобы не возиться с описанием взял стандартную микросхему.

    Код:
    module ir13
    (
       input             rst_n,         // async reset, active low
       input             clk,           // sync clock, raising edge
       input  [2:1]      s,             // shift mode
       input  [7:0]      d,             // parallel load data
       input             dsr, dsl;      // new shifted in data bit
       output reg [7:0]  q              // output data
    );
    
    wire [7:0] q_rc;
    
    //
    // assign form is used deliberately for illustrative purposes
    //
    assign q_rc[0] = (s == 2'b00) & q[0]         // storage
                   | (s == 2'b01) & q[1]         // shift left
                   | (s == 2'b10) & dsr          // shift right
                   | (s == 2'b11) & d[0];        // parallel load
    
    assign q_rc[1] = (s == 2'b00) & q[1]         // storage
                   | (s == 2'b01) & q[2]         // shift left
                   | (s == 2'b10) & q[0]         // shift right
                   | (s == 2'b11) & d[1];        // parallel load
    
    assign q_rc[2] = (s == 2'b00) & q[2]         // storage
                   | (s == 2'b01) & q[3]         // shift left
                   | (s == 2'b10) & q[1]         // shift right
                   | (s == 2'b11) & d[2];        // parallel load
    
    assign q_rc[3] = (s == 2'b00) & q[3]         // storage
                   | (s == 2'b01) & q[4]         // shift left
                   | (s == 2'b10) & q[2]         // shift right
                   | (s == 2'b11) & d[3];        // parallel load
    
    assign q_rc[4] = (s == 2'b00) & q[4]         // storage
                   | (s == 2'b01) & q[5]         // shift left
                   | (s == 2'b10) & q[3]         // shift right
                   | (s == 2'b11) & d[4];        // parallel load
    
    assign q_rc[5] = (s == 2'b00) & q[5]         // storage
                   | (s == 2'b01) & q[6]         // shift left
                   | (s == 2'b10) & q[4]         // shift right
                   | (s == 2'b11) & d[5];        // parallel load
    
    assign q_rc[6] = (s == 2'b00) & q[6]         // storage
                   | (s == 2'b01) & q[7]         // shift left
                   | (s == 2'b10) & q[5]         // shift right
                   | (s == 2'b11) & d[6];        // parallel load
    
    assign q_rc[7] = (s == 2'b00) & q[7]         // storage
                   | (s == 2'b01) & dsl          // shift left
                   | (s == 2'b10) & q[6]         // shift right
                   | (s == 2'b11) & d[7];        // parallel load
    
    always @(posedge clk or negedge rst_n)
    begin
       if (~rst_n)
          q <= 8'b00000000;
       else
          q <= q_rc;
    end
    endmodule
    Теперь надо подумать как это представить на С++, можете шаблон создать? Я в плюсах писатель не очень.
    Последний раз редактировалось Vslav; 03.04.2015 в 17:54.

  2. #532

    Регистрация
    19.04.2013
    Адрес
    г. Чебоксары
    Сообщений
    613
    Спасибо Благодарностей отдано 
    18
    Спасибо Благодарностей получено 
    176
    Поблагодарили
    136 сообщений
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Vslav Посмотреть сообщение
    vm1_timer - уже имеет внутри триггеры, его как функцию не представить.
    Вот этот момент, хоть убей, не могу понять, почему нет? vm1_timer - предполагаем что это некая микросхема с входом и выходом, её внутреннее состояние модуль vm1_qbus вообще никак не должно волновать, потому что он не использует у себя где либо ещё внутренние переменные таймера. Поэтому вообще нет никаких причин не мочь его вызвать как функцию. Просто его надо вызывать среди других alwaysов.
    Программная эмуляция предполагает расчёт состояния процессора в определённые дискретные моменты времени, только во время переднего и заднего фронтов ТЧ, в остальное время что происходит с процессором знать нет необходимости. И как-то увязывать внутренности модуля vm1_timer с внутренностями модуля vm1_qbus вообще нет никаких причин. Тем более, что эмулируется обычно математическая модель, а не электрическая.
    Цитата Сообщение от Vslav Посмотреть сообщение
    always не может быть сложным и развеститым
    Ну как не может?
    Код:
    if (cond1)
      a <= val1;
    else if (cond2)
      a <= val2;
    else if (cond3)
      a <= val3;
    В одну строку не запишешь, а если условия большие и сложные - вообще будет винегрет. А если конструкция case? Или я чего-то недопонимаю?
    Цитата Сообщение от Vslav Посмотреть сообщение
    Получится, абсолютно весь проект сводится к парам функция/триггер
    Как-то это меня смущает, что-то мне подсказывает, что раз всё так просто, значит где-то обязательно подвох какой-нибудь будет. Но попробую на таймере поэкспериментировать.
    Цитата Сообщение от Vslav Посмотреть сообщение
    можете шаблон создать?
    Имеется в виду c++ конструкции template<...>...{} ? если да - то это для меня непонятная магия и я этим функционалом не пользуюсь. А что-то другое - сделаю, чуть позже.

    А я тут заново попытался сконвертировать верилатором Async\vm1.v выделив каждый модуль в отдельный файл. В результате он почти смог, но стал ругаться так:
    Код:
    %Error-BLKANDNBLK: vm1_qbus.v:107: Unsupported: Blocked and non-blocking assignments to same variable: v.core.ir_seq
    %Error-BLKANDNBLK: vm1_qbus.v:138: Unsupported: Blocked and non-blocking assignments to same variable: v.core.abtos
    %Error-BLKANDNBLK: vm1_qbus.v:163: Unsupported: Blocked and non-blocking assignments to same variable: v.core.init_out
    Путём сравнения пришёл к выводу, что ему не нравится, что указанные переменные используются одновременно в always @(*) и always @(posedge pin_clk) кажется.

  3. #533

    Регистрация
    31.03.2013
    Адрес
    г. Киев
    Сообщений
    2,413
    Спасибо Благодарностей отдано 
    132
    Спасибо Благодарностей получено 
    759
    Поблагодарили
    353 сообщений
    Mentioned
    88 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от gid Посмотреть сообщение
    Вот этот момент, хоть убей, не могу понять, почему нет?
    Я неточно выразился - нельзя представить как комбинационную функцию. Можно представить как объект класса с внутренним состоянием. То есть assign можно выразить как просто функцию без состояния, а вот always требует памяти внутри.

    Цитата Сообщение от gid Посмотреть сообщение
    нет необходимости. И как-то увязывать внутренности модуля vm1_timer с внутренностями модуля vm1_qbus вообще нет никаких причин. Тем более, что эмулируется обычно математическая модель, а не электрическая.
    Увязывать надо в том смысле что должны правильно вычисляться комбинационные функции по всей системе в целом. А также должны щелкать все триггеры во всей системе одновременно. Например, выход таймера зависит от регистра счетчика, который по клоку готов инкрементироваться. И этот выход подключен к другому регистру внутри процессора, идет чтение регистра счетчика процессором, и регистр готов защелкнуть читаемое входное значение. Если сначала вызвать таймер - он инкрементируется, и при вызове обработки регистра-защетки чтения, будет защелкнуто неверное значение. Поэтому и предлагается решение - вычислить все функции подаваемые на триггеры, а по событию клока только перенести в триггеры.

    Цитата Сообщение от gid Посмотреть сообщение
    Ну как не может?
    Код:
    if (cond1)
      a <= val1;
    else if (cond2)
      a <= val2;
    else if (cond3)
      a <= val3;
    Тут смотря какие cond, не указаны они ли в списке чувствительности always @() (как асинхронный сброс, например). Если нет, то можно переписать так:
    Код:
      a <= cond1 ? val1 : (cond2 ? val2 : (cond3 ? val3 : a));
    Или так (для битовой переменной)
    Код:
      a <= (cond1 & val1)
         | (~cond1 & cond2 & val2)
         | (~cond1 & ~cond2 & cond3 & val3)
         | (~cond1 & ~cond2 & ~cond3 & a);
    Ну и можно просто if-ы перенести в С++ при вычислении assign, тоже будет работать.

    Цитата Сообщение от gid Посмотреть сообщение
    В одну строку не запишешь, а если условия большие и сложные - вообще будет винегрет. А если конструкция case? Или я чего-то недопонимаю?
    Тем более, сложные условия надо выносить из always, так проще для понимания. case на С++ в большинстве случаев так и опишется "в лоб". И case там у меня в двух местах всего, емнип.

    Цитата Сообщение от gid Посмотреть сообщение
    Имеется в виду c++ конструкции template<...>...{} ?
    Я имел ввиду просто класс - внутренние переменные и методы. И мы их обсудили бы, для начала на очень простом 155ИР13 - там есть что обсудить для начала на самом простом примере, потом мы эти 155ИР13 скаскадируем по-разному, снова обсудим, а там уже до таймера доберемся.

    Цитата Сообщение от gid Посмотреть сообщение
    А я тут заново попытался сконвертировать верилатором Async\vm1.v
    Следует взять ветку Qsync, она более предсказуема для моделирования, фазы сигналов в ней такие же, поэтому разницы нет, а моделятор написать будет проще. И в ветке Async есть always-ы с @(*) - на самом деле это assign , и петли обратной связи.

    Цитата Сообщение от gid Посмотреть сообщение
    Путём сравнения пришёл к выводу, что ему не нравится, что указанные переменные используются одновременно в always @(*) и always @(posedge pin_clk) кажется.
    Да, но там назначаются разные биты массива, они не перекрываются, фактически это разные отдельные переменные, ничего не нарушено и ничего подозрительного. Но, видимо, появились какие-то дополнительные ограничения для успешной автогенерации С++ кода.

  4. #534

    Регистрация
    19.04.2013
    Адрес
    г. Чебоксары
    Сообщений
    613
    Спасибо Благодарностей отдано 
    18
    Спасибо Благодарностей получено 
    176
    Поблагодарили
    136 сообщений
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от 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]. Но такое представление становится неудобным для логики редуцирования и интерпретации значения как числа.

  5. #535

    Регистрация
    31.03.2013
    Адрес
    г. Киев
    Сообщений
    2,413
    Спасибо Благодарностей отдано 
    132
    Спасибо Благодарностей получено 
    759
    Поблагодарили
    353 сообщений
    Mentioned
    88 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от gid Посмотреть сообщение
    внутри которой вызываются другие функции eval объектов класса (модулей, которые вызываются внутри другого модуля).
    Если вызываются внутри, то ОК. Но откуда они возьмут недостающие параметры? Кое-что может быть снаружи текущего модуля? Значит надо делать выходные значения модулей или доступными глобально или писать методы извлечения, или подсовывать как параметры. Или С++ это позволит как-то красиво автоматизировать?

    Цитата Сообщение от gid Посмотреть сообщение
    Эта ветка принципиально не конвертируется, т.к. не хватает модулей. В файле vm1_alib.v есть вызов модулей altsyncram и altpll, которых нету, если нету квартуса или модельсима.
    Все модули в каталоге нужны только для работы в реальной FPGA, проект готовился для компиляции в железо. Для моделирования самого процессора достаточно файлов: vm1_qbus.v, vm1_timer.v, vm1_plm.v и пара параметров из config.v. Для моделирования верхний модуль - vm1_qbus().

    Цитата Сообщение от gid Посмотреть сообщение
    Вот что получилось для ir13. Файл ir13.h
    Входные переменные запоминаются внутри объекта? А нужно ли? лишняя память и манипуляции. Если сделать метод assign (у вас он называется eval) отдельным, без параметра clk, и сделать отдельные методы always_p и always_n, то clk вообще нигде не нужен. Я бы хранил только q_rc и все. Примерно так:

    Код:
    BOOL global_change;
    
    class ir13
    {
        //
        // внутренние переменные
        //
        BYTE    q_rc;
    
    public:
        ir13 (void){};
        ~ir13(void){};
    
        void assign(
                BOOL rst_n,
                BYTE s,
                BYTE d,
                BOOL dsr,
                BOOL dsl);
    
        void always_p(void);
        void always_n(void){};
        //
        // Выходные переменные - доступны всем,
        // или написать метод извлечения
        //
        BYTE    q;
    };
    
    void ir13::assign(
                BOOL rst_n,
                BYTE s,
                BYTE d,
                BOOL dsr,
                BOOL dsl)
    {
        if (!rst_n)
        {
            q_rc = 0;
            if (q != 0)
            {
                q = 0;
                //
                // Здесь надо как-то сигнализировать всей системе что изменилось значение
                // триггеров и нужен перерасчет остальных функций в системе
                // Как вариант - глобальный флаг, или возвращаемое значение
                //
                global_change = TRUE;
            }
            return;
        }
        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;
            default:
                //
                // some debug assert
                //
        }
    
    void ir13::always_p(void)
    {
        q = q_rc;
        //
        // Здесь надо как-то сигнализировать всей системе что изменилось значение
        // триггеров и нужен перерасчет остальных функций в системе
        // Как вариант - глобальный флаг, или возвращаемое значение
        //
        // Можно и не сигнализировать, если вызывающая сторона и так знает
        // что будут обязательные изменения
        //
        global_change = TRUE;
    }
    Цитата Сообщение от gid Посмотреть сообщение
    Я не стал расписывать назначения побитно
    Угу, сейчас нам это совсем неважно.

    Update: общий расклад по эмулятору:
    Код:
    void assign_process(void)
    {
        for(;;)
        {
            global_change = FALSE;
            for each module in system
            {
                module1.assign(parameters);
                ...
                moduleN.assign(parameters);
                if (!global_change)
                {
                    break;
                }
            }
        }
    }
    
    void always_process_p(void)
    {
        for each module in system
        {
            module1.always_p();
            ...
            moduleN.always_p();
        }
    }
    
    void always_process_n(void)
    {
        for each module in system
        {
            module1.always_n();
            ...
            moduleN.always_n();
        }
    }
    
    //
    // Общий цикл эмулятора
    //
    for(;;)
    {
        //
        // Назначаем нужные входные сигналы процессора перед фронтом такта
        // Это сигналы топового модуля проекта - внешняя шина, перрывания и тд
        //
        top_input_process();
        //
        // Обрабатываем assign всех модулей
        //
        assign_process();
        //
        // Обрабатываем положительный фронт тактовой
        //
        always_process_p();
        //
        // Обрабатываем assign всех модулей
        //
        assign_process();
        //
        // Забираем результаты с выходов топового модуля
        //
        top_output_process();
        //
        // Назначаем нужные входные сигналы процессора перед срезом такта
        //
        top_input_process();
        //
        // Обрабатываем assign всех модулей
        //
        assign_process();
        //
        // Обрабатываем отрицательный фронт тактовой
        //
        always_process_n();
        //
        // Обрабатываем assign всех модулей
        //
        assign_process();
        //
        // Забираем результаты с выходов
        //
        top_output_process();
    }
    Последний раз редактировалось Vslav; 04.04.2015 в 14:50.

  6. #536

    Регистрация
    19.04.2013
    Адрес
    г. Чебоксары
    Сообщений
    613
    Спасибо Благодарностей отдано 
    18
    Спасибо Благодарностей получено 
    176
    Поблагодарили
    136 сообщений
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Vslav Посмотреть сообщение
    Но откуда они возьмут недостающие параметры?
    Какие? Разве модуль может использовать что-то, что не описано как входной параметр или как внутренняя переменная? Если может, то модуль надо переписать так чтобы он использовал только то, что прямо задано во входных параметрах.
    Цитата Сообщение от Vslav Посмотреть сообщение
    А нужно ли? лишняя память и манипуляции.
    Это потому что у нас модуль маленький и простой.
    Тут надо бы ещё определиться с такой вещью. Какая модель первична. Описанная на верилоге или на си? Т.е. Нужно ли и на си сохранять более менее понятный код, по которому можно разобраться в работе?
    Если взять что посложнее, например vm1_qbus.v, то там функция assign неприлично разрастётся и возникнет желание разбить её на несколько частей, хотя бы чтобы самому не запутаться. Но если один раз написать и забыть, и больше туда не заглядывать - то сойдёт.
    Имея одну функцию assign и целую кучу awaysов на все случаи жизни, придётся извне вызывать сперва assign, потом разные awaysы, которые необходимо, потом ещё раз assign, т.е. самим выполнить необходимую работу.
    Мой же метод предполагает, один раз вызвать одну функцию, там внутри всё само прокрутится по заданным алгоритмам, и на выходе будет конечный результат, который можно использовать снаружи.

    Теперь нужно на верилоге написать модуль, который использует модуль ir13 внутри себя, например 16 битный сдвиговый регистр или ещё чего-нибудь. И попробовать переписать всё это на си. Вот тут и станет ясно, чей метод нужно будет использовать в дальнейшем.

  7. #537

    Регистрация
    31.03.2013
    Адрес
    г. Киев
    Сообщений
    2,413
    Спасибо Благодарностей отдано 
    132
    Спасибо Благодарностей получено 
    759
    Поблагодарили
    353 сообщений
    Mentioned
    88 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от gid Посмотреть сообщение
    Какие? Разве модуль может использовать что-то, что не описано как входной параметр или как внутренняя переменная? Если может, то модуль надо переписать так чтобы он использовал только то, что прямо задано во входных параметрах.
    В-общем случае можно "достать" переменные из других модулей, но я таким не злоупотреблял. То есть вопрос закрыт.

    Цитата Сообщение от gid Посмотреть сообщение
    Это потому что у нас модуль маленький и простой.
    Я все равно не вижу причин чтобы хранить входные переменные внутри модуля - заходят они все равно все сразу кучкой, а детектор фронтов нужен только на clk, проще выделить события clk в отдельные методы.

    Цитата Сообщение от gid Посмотреть сообщение
    Тут надо бы ещё определиться с такой вещью. Какая модель первична. Описанная на верилоге или на си? Т.е. Нужно ли и на си сохранять более менее понятный код, по которому можно разобраться в работе?
    Это смотря какие планы по дальнейшему использованию эмулятора. Лично я, возможно, буду использовать чтобы разобраться с микрокодом, на подходе еще микрокод от ВМ1Г. Мне некритично, я могу и с модельсимом поработать. Но, весьма вероятно, что найдутся еще люди которым будет интересно тоже на работу микрокода посмотреть и поизучать как работают отдельные инструкции. В любом случае, получится очень точный потактовый эмулятор ВМ1, который не надо будет отлаживать на предмет совпадения с оригиналом - это получается автоматически.

    Цитата Сообщение от gid Посмотреть сообщение
    Если взять что посложнее, например vm1_qbus.v, то там функция assign неприлично разрастётся и возникнет желание разбить её на несколько частей, хотя бы чтобы самому не запутаться. Но если один раз написать и забыть, и больше туда не заглядывать - то сойдёт.
    Да вроде там нет очень глубокого вложения функций, да и файл основной всего ~2K строк. Для начала можно написать как есть, там видно будет, интересно это кому то еще или нет.

    Цитата Сообщение от gid Посмотреть сообщение
    Имея одну функцию assign и целую кучу awaysов на все случаи жизни,
    Так always всего два - про положительному и отрицательному фронту такта, и в каждом из блоков один из них пустой. То есть - по факту всего два метода и никакого запоминания входных сигналов, никаких детекторов и прочего.

    Цитата Сообщение от gid Посмотреть сообщение
    придётся извне вызывать сперва assign, потом разные awaysы, которые необходимо, потом ещё раз assign, т.е. самим выполнить необходимую работу.
    Да, я там в апдейте предыдущего поста подробно основной цикл расписал.

    Цитата Сообщение от gid Посмотреть сообщение
    Мой же метод предполагает, один раз вызвать одну функцию, там внутри
    Да, функция одна, но тут показан далеко не весь код - нет видно кто и как вызывает ее, формирует входные параметры и где хранит результат. Это я к вопросу создания снашлота чтобы обеспечить "одновременность". В моем случае функции две, но параметры нужны только одной и их можно тупо брать сразу из выходных переменных модулей, не парясь о снапшоте. То есть - данные модуля хранятся в самом же модуле и нигде больше - самое соответствие парадигме ООП, имхо.

    Цитата Сообщение от gid Посмотреть сообщение
    всё само прокрутится по заданным алгоритмам, и на выходе будет конечный результат, который можно использовать снаружи.
    Насчет "само" я не понял, будет сгенерирован какой-то неявный С++ код чтобы "прокрутить по алгоритмам" ? Или эта реализация таки будет возложена на вызывающую сторону - обдумать где взять параметры (текущие/снапшот) и куда сложить результат?


    Цитата Сообщение от gid Посмотреть сообщение
    Теперь нужно на верилоге написать модуль, который использует модуль ir13 внутри себя, например 16 битный сдвиговый регистр
    Сделал 16-битный сдвиговый регистр, младший байт может загружаться параллельно и потом сдвигаться в старший байт, в зависимости от переменной mode_s. Последовательно выводимые данные из старшего разряда печатаются на консоль. Пример большой, но больше половины это создание внешних сигналов, сама эмуляция вполне простая.

    Код:
    ir13    low_byte;
    ir13    high_byte;
    BOOL    global_change;
    
    BOOL    rst_n;
    BYTE    mode_s;
    BYTE    parallel_data_in;
    BOOL    serial_data_in;
    
    void main(void)
    {
        //
        // Initial reset, both registers
        //
        rst_n   = 0;
        mode_s  = 0;
        parallel_data_in    = 0;
        serial_data_in      = 0;
        generate_event(EVENT_INPUT_CHANGE);
        //
        // Parallel load in low-byte, high byte always shifts
        //
        rst_n               = 1;
        parallel_data_in    = 0x55;         // load 'U' character
        mode_s              = 3;            //
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    //
    // Not needed in this example
    //
    //  generate_event(EVENT_CLOCK_FALL);
    //
        mode_s              = 2;
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    
        mode_s              = 3;
        parallel_data_in    = 0x53;         // load 'S' character
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    
        mode_s              = 2;
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    
        mode_s              = 3;
        parallel_data_in    = 0x42;         // load 'B' character
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);
    
        mode_s              = 2;
        generate_event(EVENT_INPUT_CHANGE);
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
        generate_event(EVENT_CLOCK_RISE);   printf("%c", (high_byte.q & 0x80) ? '1' : '0');
    }
    
    void assign_process(void)
    {
        for(;;)
        {
            global_change = FALSE;
    
            low_byte.assign(rst_n, mode_s, parallel_data_in, serial_data_in, 0);
            high_byte.assign(rst_n, 2, 0, (low_byte.q & 0x80), 0);
    
            if (!global_chabge)
            {
                break;
            }
        }
    }
    
    void always_process_p(void)
    {
        low_byte.always_p();
        high_byte.always_p();
    }
    
    void always_process_n(void)
    {
        low_byte.always_n();
        high_byte.always_n();
    }
    
    //
    // Отдельный поток/функция, обрабатывающая события эмулятора:
    //  - измнение входных сигналов
    //  - положительный и отрицательный фронты тактовой
    //
    for(;;)
    {
        switch(wait_event())
        {
            case EVENT_INPUT_CHANGE:
            {
                //
                // Назначаем нужные входные сигналы всей схемы
                // Обрабатываем assign всех модулей
                //
                assign_process();
                break;
            }
            case EVENT_CLOCK_RISE:
            {
                //
                // Обрабатываем положительный фронт тактовой
                //
                always_process_p();
                assign_process();
                break;
            }
            case EVENT_CLOCK_FALL:
            {
                //
                // Обрабатываем отрицательный фронт тактовой
                //
                always_process_n();
                assign_process();
                break;
            }
        }
    }

  8. #538

    Регистрация
    19.04.2013
    Адрес
    г. Чебоксары
    Сообщений
    613
    Спасибо Благодарностей отдано 
    18
    Спасибо Благодарностей получено 
    176
    Поблагодарили
    136 сообщений
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Vslav Посмотреть сообщение
    далеко не весь код - нет видно кто и как вызывает ее, формирует входные параметры и где хранит результат
    Это потому что я пока и сам до конца не представляю, как оно всё должно быть.
    Цитата Сообщение от Vslav Посмотреть сообщение
    Насчет "само" я не понял
    Имеется в виду, что не я буду заниматься вызовом assignов и alwaysов, а функция.

    Я попробовал на таймере вынести из always выражения в assign, в этом случае, делать снапшот уже не нужно, но кол-во переменных увеличивается на кол-во переменных-регистров. И если не знать, что это за код, сходу уже не догадаешься, что это такое. При этом ваш метод передачи параметров выглядит более оправданным.

    Цитата Сообщение от Vslav Посмотреть сообщение
    Сделал 16-битный сдвиговый регистр
    А где верилог? Я ещё не до конца отказался от своего метода и хочу ещё убедиться в его оправданности. Или неоправданности. К тому же я всё равно не понимаю, как работает верилоговская модель, я просто механически перевожу код на си. И для меня правильность методики определит только практический эксперимент.
    И мне важно провести сопоставление именно вызова одного верилоговского модуля из другого.
    Последний раз редактировалось gid; 04.04.2015 в 18:11.

  9. #539

    Регистрация
    31.03.2013
    Адрес
    г. Киев
    Сообщений
    2,413
    Спасибо Благодарностей отдано 
    132
    Спасибо Благодарностей получено 
    759
    Поблагодарили
    353 сообщений
    Mentioned
    88 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от gid Посмотреть сообщение
    А где верилог?
    Код:
    module ir16
    (
       input       rst_n,
       input       clk,
       input [2:1] mode_s,
       input [7:0] parallel_data_in,
       input       serial_data_in,
       output      serial_data_output
    );
    reg [7:0]      l, h;
    
    assign serial_data_output = h[7];
    
    ir13 low_byte(
       .rst_n(rst_n),
       .clk(clk),
       .s(mode_s),
       .d(parallel_data_in),
       .dsr(serial_data_in),
       .dsl(1'b0)
       .q(l2h));
    
    ir13 high_byte(
       .rst_n(rst_n),
       .clk(clk),
       .s(2'b10),
       .d(8'b00000000),
       .dsr(l[7]),
       .dsl(1'b0)
       .q(h));
    endmodule
    Цитата Сообщение от gid Посмотреть сообщение
    И мне важно провести сопоставление именно вызова одного верилоговского модуля из другого.
    Да, это минимизирует количество ошибок переноса. Но, имхо, "в лоб" оно плохо переносится. Верилог - это попытка синтаксисом С как-то описать связи аппаратуры. В-общем-то получилось, но часто напоминает "тут - играть, тут не играть, а тут рыбу заворачивали"

    Общая схема такая - есть набор переменных + тактовый сигнал. Часть из этого набора переменных представляют внешние входные сигналы проекта. Остальное - внутренние триггеры. Входы триггеров представляют собой комбинационые функции от набора переменных (внешних + триггеры). Если меняются переменные в наборе:

    - изменение входных сигналов
    - изменение состояния триггеров

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

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

    Выходные сигналы проекта также представляют собой комбинационные функции от набора переменных (внешние входы + внутренние триггеры) и должны пересчитываться по тем же правилам как входные функции триггеров.

    Каким методом такую систему реализовать - не суть важно.

  10. #540

    Регистрация
    19.04.2013
    Адрес
    г. Чебоксары
    Сообщений
    613
    Спасибо Благодарностей отдано 
    18
    Спасибо Благодарностей получено 
    176
    Поблагодарили
    136 сообщений
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    А вот моё видение, как это примерно должно выглядеть. ir16smpl.rar
    В архиве модели ir13, ir16 и примерное устройство, которое использует ir16. Избыточность и многие ненужные действия я убирать таки не стал. По мне, так более наглядно, в ущерб производительности.
    Чтобы всё работало, надо где-то создать объект CSampleDevice m_Device;
    и циклично вызывать его функцию m_Device.eval_p();
    Один вызов этой функции - один такт работы всего устройства, включая всё, из чего оно состоит.
    И тут в полный рост вылезла проблема, которую я не могу решить уже два года. Никак не получается организовать отдельный рабочий поток на основе класса, которым можно управлять с помощью сообщений и в котором где-то задать рабочий цикл, в котором бы вызывалось m_Device.eval_p(); с заданной частотой. Как это сделать я не знаю, где подсмотреть - тоже не знаю.

Страница 54 из 273 ПерваяПервая ... 505152535455565758 ... ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Похожие темы

  1. Ответов: 7
    Последнее: 28.06.2014, 17:50
  2. Микросхемы МПК 580,1801,1810 и другие...
    от Alex_Vac в разделе Барахолка (архив)
    Ответов: 44
    Последнее: 07.04.2012, 08:03
  3. ЦИФРОВАЯ МУЗЫКА НА ZX
    от AAA в разделе Музыка
    Ответов: 98
    Последнее: 18.09.2011, 22:33
  4. Учебный стенд УМПК-1801
    от dk_spb в разделе ДВК, УКНЦ
    Ответов: 2
    Последнее: 12.05.2010, 16:52
  5. Цифровая музыка от Вадима Ермеева
    от zxmike в разделе Музыка
    Ответов: 2
    Последнее: 06.08.2007, 23:13

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •