Pull to refresh

Comments 30

О ужос.

rxIN счелкать на входе надо, прежде чем отправлять его на вход конечного автомата. А вообще принято triple majority делать.

В делителе считать лучше вниз до нуля. Тогда, когда вы сделаете возможность задавать baud rate регистром, он меньше жрать будет.

Сдивговые регистры тоже красивее смотрятся. Ну и по старт-биту в старшем разряде сдвигового регистра можно определять конец приёма. С передачей счётчик нужен, так уж и быть.

Ну и раз уж статью делайте, то где поддержка parity bit, flow control, приёмный и передающий буфера?:)
Сеньор принципал верилог девелопер детектед.
Ну и раз уж статью делайте, то где поддержка parity bit, flow control, приёмный и передающий буфера?:)

Ну так хотелось сделать все как можно проще :) Что-бы влезало в 240 ячеек Max II EPM240, и осталось место под полезную логику. Ну flow control и буфера можно внешне прикрутить, по надобности. Parity bit не делал :)
На счет rxIN. Нужно поставить пару-тройку регистров, как-то так?

reg [1:0] rxLock = 2'b11;
wire rx = (|rxLock);
always @(posedge clockIN) begin
    rxLock = rxLock << 1;
    rxLock[0] = rxIN;
end

О ужос.

А чего, неужто все так плохо?
Да, все оч плохо.
Ну в смыслп я бы такую статью не публиковал.
if(rxIN & rxIdleOUT) begin rxClkCounter <= 0;

здесь вы по сути описываете конечный автомат, а rxIN берете прямо со входа. Соответственно, когда фронт rxIN придет очень близко к фронту клока, rxClkCounter потенциально может принять рандомное значение. На симуляциив RTL вы этого не заметите, а вот в железе будет глючить. Ну или можете поэксперементировать с моделированием нет листа с подключенным Back annotation

if(~nRxResetIN) begin rxReg[8] <= 1'b0; rxIndex <= 4'h9; end else if(~rxIdleOUT) begin rxReg[rxIndex] = rxIN; rxIndex = rxIndex + 1'b1; end

Здесь лучше подойдёт сдвиговый регистр:

`
reg [8:0] rxShReg;

always @(posedge clk ...)

if (clr)
rxShReg <= #T 9'h1FF;
else
if (rx_do_shift)
rxShReg <= #T {rxShReg[7:0], rx_in};
`

Если в него же задвигать стартовый бит, то по нулю в txShReg[8] будете ловить конец приёма.
rx_in лучше сделать выходом маожоритарной схемы ((a&b)|(a&c)|(b&c)), где a,b,c — входной сдвиговы регистр. Тогда отфильтруете возможные глитчи на длинных проводах, или косяки, связанные с длинными фронтами.

С отправкой такая же шляпа:
txPin = txData[txIndex];
у вас здесь вместо изящненького сдвигового регистра, мультиплексор на 8 входов и еще один регистр на выходе. Ничего страшного, если вы пишите UART для макса, но становится страшно за код, который вы родите в более серьезном проекте :)
Все приму к сведению. По поводу сдвигового регистра согласен полностью, нужно переписать.
Но, в этом блоке как раз и решается проблема с захватом стартового бита:

if(rxIN & rxIdleOUT) begin
    rxClkCounter <= 0;
    rxBaudClk    <= 0;
end

Т.е. если мы захватили стартовый бит, мы будем инкрементировать счетчик делителя для клока как минимум 8 раз при лог 0 на rxIN, пока не стартует прием. Если мы поймаем глитч, а потом линия rxIN вернется в лог. 1, то rxClkCounter сбросится в 0, и будет ждать нового спада на линии rxIN, что-бы опять посчитать эти 8 клоков перед начало захвате. По моему скромному мнению это решение на много эффективнее, т.к. попросту экономит три регистра. Но опять-же я могу ошибаться.
Хорошо, я все понял. Переписал код. На днях буду править статью.
А чего, неужто все так плохо?

Ничего хорошего. Пользуйтесь в синхронных always только неблокирующими присваиваниями, иначе будут образовываться защёлки (latch), что ведёт к куче негативных последствий.
Можно поподробней, какие могут быть негативные последствия при данной реализации?
Защёлки приводят к разным труднопрогнозируемым багам (glitches и т.п.), поэтому лучше просто так не делать вообще никогда. Синтезатор должен, кстати, выдавать предупреждение об этом. Вы в Quartus II делали?
Да, в квартусе. Предупреждений не было по этому поводу. Просто я не вижу другого варианта, в данной ситуации.
В любой ситуации можно сделать схему без защёлок.
Во-первых: output wire — это не есть хорошо. Выходы настоятельно рекомендуется делать reg-ами.
Так он там и есть регистр, вернее назначен на регистр, иначе и работать не будет. А есть ссылка на литературу, где рекомендуется использовать на выходе именно регистр?
Просто я так себе представляю, что модуль — это интерфейс. И про реализацию его пользователь знать не должен. Какие выходы на какие регистры назначены, и через какие лог. примитивы, и т.п.
Очень рекомендую: Evgeni Stavinov "100 Power Tips for FPGA Designers"
Я лично знаю Ставинова в реале.

При этом вы перепутали: проблема c блокирующим присваиванием в синхронных always-блоках вовсе не latch (он возникнуть в них не может), а race condition — зависимость результатов от порядка симуляции always-блоков:

Вот иллюстрация проблемы с race conditions:

http://www.silicon-russia.com/2015/03/19/intro-rtl-fpga-verilog-midterm-1-1/

module dut
(
    input              clk,
    input        [7:0] d,
    output logic [7:0] q
);
    logic [7:0] r;

    always @(posedge clk)  // ПЛОХО! Результат симуляции зависит от того, какой блок симулируется раньше в event queue
        r = d;

    always @(posedge clk)
        q = r;

endmodule

Вот иллюстрация проблемы с latch:

always @ (posedge clock)
    if (a)
        b <= c;   // Получается D-flip-flop (D-триггер)

always @ (a or c)
    if (a)
        b = c;   // Получается latch (защелка)

Вот правила:

7.2 Which rule for signal assignment is violated in the following code?

a) Synchronous sequential logic: use always @(posedge clk) or always_ff @(posedge clk)
and nonblocking assignments (<=)

    always_ff @ (posedge clk)
       q <= d; // nonblocking

b) Simple combinational logic: use continuous assignments (assign.)

         assign y = a & b; 

c) More complicated combinational logic: use always @* or always_comb and blocking assignments (=)

d) Assign a signal in only one always statement or continuous assignment statement

e) This code does not violate any rules for signal assignment

Вообще вот все варианты

http://silicon-russia.com/exams_and_quizes/2015_08_08_quiz_4_ff/2015_08_08_quiz_4_ff.html

В output wire ничего плохого нет, оно может идти с более нижнего уровня иерархии или быть подсоединено к регистру.
Да практически всегда на выходе должен быть регистр. Иначе, если это комбинационная схема, то результат на её выходе будет с задержкой.
Почитайте немного, должно проясниться немного, как было у меня:
https://habrahabr.ru/post/254869/
https://habrahabr.ru/company/metrotek/blog/235037/

Вообще, тут передача получается из одного устройства в другое. Эти устройства не имеют единого клока, так что работать ИМХО надо как при передаче сигнала между двумя клоковыми доменами.
http://www.fpga4fun.com/CrossClockDomain.html
То есть по своей тактовой частоте записывать в регистр. А уже с данными на его выходе работать.
Да практически всегда на выходе должен быть регистр. Иначе, если это комбинационная схема, то результат на её выходе будет с задержкой

Синтаксис "output wire" вовсе не означает, что это выход от комбинационной схемы.

Например этот вывод может идти от более нижнего уровня иерархии или быть подсоединенным к регистру:


input i,
output wire o
);

reg r;

always @ (posedge clock)
r <= i;

assign o = r; // Так будет работать и вывод будет из регистра

Задержка с delta-cycle — не реальная задержка
Синтаксис «output wire» вовсе не означает, что это выход от комбинационной схемы

Я имел в виду именно в физическом смысле. Более того, у меня чаще wire на выходе. Я не знаю, какой кодстайл принят у профессионалов, но у меня часто выходы wire а внутри уже логика, результат которой <= на регистры, а их assign наружу. Если модуль больше средних размеров, то это мне помогает обозначить порты модуля и дальше их уже не трогать (не менять с wire на reg или обратно, в процессе доработок). То есть получается, что если я что-то и меняю, то только касаемо внутренностей модуля.

Тут я еще один момент заметил — Hypnotriod:
Просто я так себе представляю, что модуль — это интерфейс. И про реализацию его пользователь знать не должен. Какие выходы на какие регистры назначены, и через какие лог. примитивы, и т.п.

Я ж, как новичек, тоже столкнулся с этим вопросом. Ведь можно написать чисто комбинационный модуль, без регистров. А потом, к примеру, результат защелкивать в регистр на верхнем уровне. И если совсем не знать, что внутри модуля, то может оказаться, что в модуле все же есть регистр, и запись его выхода в регистр на более высоком уровне может оказаться не нужной и синтезатор сократит лишний регистр. И произойдет то, что я не понимаю, что случилось. Помогло систематическое использование RTL Viewer и чтение книжек.

Поэтому, если модуль большой, и пользователь действительно не должен знать, как он внутри работает, то внутри я ставлю регистры на выходе и после этого никакой комбинаторики. Иначе уже синтезатор начинает ругаться, причем по-делу (забыл ошибку). Исключение у меня составляют небольшие вспомогательные модули. Они входят в крупные модули (с целью сократить код), в схему комбинаторики перед записью в выходные регистры. Но они, как правило, занимают не больше пол экрана, и нет никакой проблемы в них зайти и понять, что там регистров нет.
Это все хорошо, но rxReadyOUT даст лог. 1 после того, как мы примем стоповый бит, т.е. принятые данные уже давно на своих местах. И не важно, завязан он на комбинаторной логике или на выходе регистра. Вы напишите конкретную строчку, и скажите почему она, по вашему мнению, не будет работать. А то так мы рассыждаем на абстрактные темы.
Просто я все сейчас пытаюсь найти подводные камни, в моей реализации :)
Спасибо за статью. Скоро как раз надо будет делать небольшой приемник RS232 на ПЛИС MAX ii 240. Пригодится. То, что есть параметризация — это хорошо, это правильно. А проблемы, я думаю, быстро исправите.
Надо было назвать статью UART TTL. Т.к. на стандарт RS232 я и не целился. У меня нет возможности его тестировать, попросту некуда подключать :) Тут скорее пойдет для подключения ПЛИС к тому-же МК.
В моем случае будет вообще прием MIDI сообщений :)
Довольно много логики будет :) Там и системные сообщения, и realtime сообщения, и running status с note off при нулевой velocity. Я делал синтезатор под МК, то хорошо наигрался. Другое дело принимать MIDI сообщения по USB, там все просто, за исключением самого USB :)
Да у меня уже все готово. Только модуль приема UART более "жирный".
Господа, товарищи, ребята! Если цель статьи потренировать свои навыки в проектировании на плис, то вопросов нет. +1. Но если вы делаете реальный проект, то зачем изобретать велосипед? Есть замечательный ресурс opencores.org/, там есть испытанные проверенные реализации от самых простых, до навороченных. Мне же импонирует (вставляю в свои проекты) реализация от
www.fpga4fun.com
Я с вами не согласен. У блока на opencores может быть неподходящий для вас интерфейс, блок может быть плохо оптимизирован, иметь отнюдь не индустриальное качество. Для небольшого блока иногда проще написать свой, чем приспособить и верифицировать чужой.
Тем не менее, это модули, на которые смотрела не одна сотня глаз. Если речь идет о "начинающем" разработчике, то сомневаюсь, что потребность есть в "индустриальном качестве". К тому же, учиться лучше на чужих ошибках.
Так я и не спорю. Есть решения на github которые уже несколько лет поддерживаются и усовершенствуются. Я не буду отрицать что данная реализация далеко не "идеальна" :) Как-раз моя задача это знакомство с ПЛИС.
Only those users with full accounts are able to leave comments. Log in, please.