Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Внешние прерывания
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Jenya7
Я пытаюсь включить внешние прерывания в свою программу на STM32F103VB.
CODE
void ExtIntInit(void)
{
// enable clock for Alternate Function
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;

//BUT - PE0
GPIOE->CRL &= ~GPIO_CRL_MODE0; //Configure as input
GPIOE->CRL &= ~GPIO_CRL_CNF0;
GPIOE->CRL |= GPIO_CRL_CNF0_1; //input with pull-up/pull-down resistor
GPIOE->BSRR |= GPIO_BSRR_BS0; //set pull-up
//ENC1 - PE1
GPIOE->CRL &= ~GPIO_CRL_MODE1; //Configure as input
GPIOE->CRL &= ~GPIO_CRL_CNF1;
GPIOE->CRL |= GPIO_CRL_CNF1_1; //input with pull-up/pull-down resistor
GPIOE->BSRR |= GPIO_BSRR_BS1; //set pull-up
//ENC2 - PE2
GPIOE->CRL &= ~GPIO_CRL_MODE2; //Configure as input
GPIOE->CRL &= ~GPIO_CRL_CNF2;
GPIOE->CRL |= GPIO_CRL_CNF2_1; //input with pull-up/pull-down resistor
GPIOE->BSRR |= GPIO_BSRR_BS2; //set pull-up

//interrupt source PE
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0_PE; //PE0 - BUT
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI1_PE; //PE1 - ENC1
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI2_PE; //PE2 - ENC2
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI3_PE; //PE3 - ENC3

//interrupt edge
EXTI->RTSR |= 0; //EXTI_RTSR_TR0; //rising
EXTI->FTSR |= EXTI_FTSR_TR0; //falling
EXTI->FTSR |= EXTI_FTSR_TR1; //falling
EXTI->FTSR |= EXTI_FTSR_TR2; //falling
EXTI->FTSR |= EXTI_FTSR_TR3; //falling

//enable ext interrupt
EXTI->IMR |= EXTI_IMR_MR0;
EXTI->IMR |= EXTI_IMR_MR1;
EXTI->IMR |= EXTI_IMR_MR2;
EXTI->IMR |= EXTI_IMR_MR3;

NVIC_EnableIRQ(EXTI0_IRQn);
NVIC_EnableIRQ(EXTI1_IRQn);
NVIC_EnableIRQ(EXTI2_IRQn);
NVIC_EnableIRQ(EXTI3_IRQn);
}

И обработчик
CODE
void EXTI0_IRQHandler(void) //button pressed
{
EXTI->PR = EXTI_PR_PR0; //clear flag???
TIM4->CNT = 0;
TIM4->CR1 |= TIM_CR1_CEN;
//but_int = true;
//NVIC_EnableIRQ(TIM7_IRQn);

}
void EXTI1_IRQHandler(void) //encoder1
{
EXTI->PR = EXTI_PR_PR1; //clear flag???
enc1_int = 1;
if(DIR1)
{ mot1_position++; }
else
{
if(mot1_position > 0)
{ mot1_position--; }
}
}

void EXTI2_IRQHandler(void) //encoder2
{
EXTI->PR = EXTI_PR_PR2; //clear flag???
if(DIR2)
{ mot2_position++; }
else
{
if(mot2_position > 0)
{ mot2_position--; }
}
}

void EXTI3_IRQHandler(void) //encoder3
{

EXTI->PR = EXTI_PR_PR3; //clear flag???
if(DIR3)
{ mot3_position++; }
else
{
if(mot3_position > 0)
{ mot3_position--; }
}
}

До этого кода все работает нормально. После включения этого куска кода камень умирает и не реагирует ни на что. Я уже пару дней бьюсь над проблемой. Читаю документацию вдоль и поперек не могу понять где проблема.
AHTOXA
GPIO затактировать забыли?
Jenya7
Цитата(AHTOXA @ Jul 27 2014, 11:55) *
GPIO затактировать забыли?

нет, я тактирую порты в другой функции
Genadi Zawidowski
выясните для начала, нет ли ситуации что "камень" не снимает запрос на прерывание - поставьте ногодрыг в обработчики прерывания от изменения порта - всё ли нормально? А вообще смотрите вот: http://188.134.5.254/browser/hfreceiver/trunk/pio.c#L122
Jenya7
Цитата(Genadi Zawidowski @ Jul 27 2014, 16:02) *
выясните для начала, нет ли ситуации что "камень" не снимает запрос на прерывание - поставьте ногодрыг в обработчики прерывания от изменения порта - всё ли нормально? А вообще смотрите вот: http://188.134.5.254/browser/hfreceiver/trunk/pio.c#L122


Я так понимаю что EXTI->PR = EXTI_PR_PR0; сбрасывает запрос.
Спасибо за код.
smk
Цитата(Jenya7 @ Jul 27 2014, 13:16) *
Я так понимаю что EXTI->PR = EXTI_PR_PR0; сбрасывает запрос.
Спасибо за код.


EXTI->PR = EXTI_PR_PR0; - как-то странно выглядит.
Вот пример обработчика. Может поможет чем.

Код
void EXTI0_IRQHandler (void)                        
{
    EXTI->PR |= (1UL<<0);       // Сбросить флаг EXTI0  

}
adnega
Цитата(smk @ Jul 27 2014, 16:33) *
EXTI->PR |= (1UL<<0); // Сбросить флаг EXTI0

Так точно делать нельзя.
Бит в EXTI->PR сбрасывается записью в него единицы. Операция "|=" сбросит все установленные биты.
smk
Цитата(adnega @ Jul 27 2014, 15:46) *
Так точно делать нельзя.
Бит в EXTI->PR сбрасывается записью в него единицы. Операция "|=" сбросит все установленные биты.

Но ведь не сбрасывает же.
adnega
Цитата(smk @ Jul 27 2014, 18:04) *
Но ведь не сбрасывает же.

Может и лишнего сбросить.
Цитата
EXTI->PR = EXTI_PR_PR0;

- годная конструкция.
Из кода не понятна судьба ноги PE3 - ее, вроде, не инициализоровали?
smk
Цитата(adnega @ Jul 27 2014, 17:09) *
Может и лишнего сбросить.

- годная конструкция.
Из кода не понятна судьба ноги PE3 - ее, вроде, не инициализоровали?

Код
#define  EXTI_PR_PR0                         ((uint32_t)0x00000001)        /*!< Pending bit for line 0 */

Тогда уже
Код
EXTI->PR |=EXTI_PR_PR0;

Хотя конечно если принять во внимание что сброс записью единицы...
Jenya7
Цитата(smk @ Jul 27 2014, 20:39) *
Код
#define  EXTI_PR_PR0                         ((uint32_t)0x00000001)        /*!< Pending bit for line 0 */

Тогда уже
Код
EXTI->PR |=EXTI_PR_PR0;

Хотя конечно если принять во внимание что сброс записью единицы...

Вставив метку я обнаружил что камень не выходит из прерывания, причем что так
Код
EXTI->PR =EXTI_PR_PR0

что так
Код
EXTI->PR |=EXTI_PR_PR0

ничего не помогает

По моему я нашел в чем проблема. У меня на этих ногах сидит синус частотой 41.5 Мгц. Буду разбираться как он там появился.
adnega
Цитата
что так что так

В таком случае предлагаю попробовать еще один "рабочий" вариант EXTI->PR |= 0 wink.gif
И оставить в итоге EXTI->PR = EXTI_PR_PR0, когда синус с этих ног уйдет.
Во всем остальном соглашусь с smk - код очень странный из-за смеси SPL и доступа к регистрам.
Куда читабельнее записать просто EXTI->PR = (1 << 0)

PS. В cortex-m хорошо, что прерывания защелкиваются в противном случае потери из-за "|=" были бы гарантированы.
PS2. По хорошему еще нужно проверять есть ли условие
Код
void EXTI9_5_IRQHandler(void)
{
    cnt1++;
    if(EXTI->PR & (1 << 6))
    {
        cnt2++;
        EXTI->PR = (1 << 6);
    }
}

Без этого никак при консолидированных обработчиках сразу от нескольких источников. И в некоторых случаях возможно повторное вхождение в прерывание - данную тему затрагивали на форуме, и некоторые участники, можно сказать, ощутили это явление на "собственной шкуре" (я в их числе).
Например, попробуйте на высоких уровнях оптимизации очищать флаг в самом конце прерывания без предварительной проверки условия.
Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6...
Сергей Борщ
Цитата(adnega @ Jul 27 2014, 19:45) *
Например, попробуйте на высоких уровнях оптимизации очищать флаг в самом конце прерывания без предварительной проверки условия.
Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6...
Чтобы этого не происходило надо добавить после сброса флага инструкцию DSB().
Golikov A.
Цитата
PS. В cortex-m хорошо, что прерывания защелкиваются в противном случае потери из-за "|=" были бы гарантированы.


Можно чуть подробнее что есть защелкиваются?
Потери от |= появляются в следствие того, что если в самом регистре уже стоят еще какие то флаги, то вы им их сбросите. Это старая и разжеванная тема почему прерывания надо сбрасывать через равно, а не через |= &= (для сбросов нулем)

Цитата
Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6...

ЧуднО это как-то, может это связано с обработкой 6 прерывания в функции с именем 5? Ну то есть есть еще какое-то объяснение? А то как так может быть что вы попадаете в прерывание от единственного источника, при этом флага этого прерывания не стоит?

Ай... ну вот Сергей уже и ответил sm.gif
adnega
Если использовать EXTI не самоцель, то для получения положения энкодера в STM32 очень хорошо подходит таймер.
Без прерываний по каждому изменению положения - полностью аппаратно.
Jenya7
Проблема закралась с неожиданной стороны. На плате были опциональные перемычки, которые старательная паяльщица тоже запаяла. В результате образовалась обратная связь которая осцилировала с бешенной частотой забивая контроллер. Интерапт молотил безостановочно.
Но все не напрасно, благодаря этому я узнал как правильно чистить интерапт. Так что спасибо вам друзья. sm.gif
Jenya7
Цитата(adnega @ Jul 28 2014, 02:18) *
Если использовать EXTI не самоцель, то для получения положения энкодера в STM32 очень хорошо подходит таймер.
Без прерываний по каждому изменению положения - полностью аппаратно.

Ноги на которых сидят таймера заняты. Кроме того 2 таймера у меня ушли на PWM.
hd44780
Jenya7, посмотрите, может ремап Вам поможет.
Мне на F105 помогло пару раз.
Jenya7
Цитата(hd44780 @ Jul 28 2014, 16:07) *
Jenya7, посмотрите, может ремап Вам поможет.
Мне на F105 помогло пару раз.

Я смотрел ремап, таймера сидят вместе с другими важными модулями.
adnega
Цитата(Jenya7 @ Jul 28 2014, 14:16) *
Я смотрел ремап, таймера сидят вместе с другими важными модулями.

Жаль. Значит энкодеру не повезло - будет самым "глючным", т.к. все ушло важным модулям.
С одной стороны можно не обращать внимание - вряд ли энкодер будут крутить часто.
С другой стороны - это средство взаимодействия с пользователем и если энкодер будет работать неустойчиво - многим это не понравится.
Например, купил я оциллогаф SDS7102, и со старой прошивкой его энкодеры просто сносили башню: крутишь в одну сторону, а реагирует не так как ожидалось.
В новой проше поправили. Сейчас отлично все работает, плюс ускорение приделали - очень удобно))
Давным-давно делал энкодер на atmega8 опросом (t=10ms) - до сих пор все работает без замечаний (а это турникеты KABA на оживленной проходной).
Если скорось большая не нужна, то лучше делать опросом, чтобы не повторилась ситуация с мегагерцами (мало ли что там может появиться).
Удачи.
KnightIgor
Цитата(Jenya7 @ Jul 28 2014, 07:50) *
Но все не напрасно, благодаря этому я узнал как правильно чистить интерапт. Так что спасибо вам друзья. sm.gif

Кстати, о чистке. У STM есть определенный "разброс и шатание" в части, как сбрасывать биты в регистрах флагов прерываний и статуса: в некоторых случаях для сброса битов регистра надо записать в другой, связаный, регистр единицы для сброса, а в некоторых случаях - нули в сам регистр, биты которого надо сбросить. То есть, для первого случая это конструкция REG = BIT; как это для EXT, а во втором случае REG = ~BIT; Поэтому надо постоянно обращать внимание на особенности регистра. Например, в доке на какой-нибудь SR (регистр статуса) при описании бита может стоять rc1_w0, что означает, что бит сбрасывается записью в него нуля. Вроде логично, но применять надо не SR &= ~BIT, а именно SR = ~BIT. Из этого следует, что удобно применить SR = ~SR, если надо одним махом сбросить все установленные биты в SR, не заморачиваясь на константы в правой части выражения wink.gif .
Jenya7
Я тут посмотрел...возможно я таки найду свободные пины для таймера.
А насчет чистки битов, может как в AVR?
Код
#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))


Кстати я не вижу большого криминала обрабатывать энкодер на прерываниях. На AVR прекрасно работало, а STM32 все таки в 5 раз быстрее.
Golikov A.
Опять нужна лекция... попробуем укороченный вариант


возникает прерывание 1 и 2, ставиться флаг 1 и 2 биты.
FLAG_REG = 0x03;
вы входите в обработчик первого прерывания
в нем пишите
FLAG_REG |= 0x01; для сброса этого флага,
получаете
FLAG_REG = FLAG_REG | 0x01 = 0x03 | 0x01 = 0x03
то есть сбрасываете оба флага, теряете второе прерывание

теперь для сброса нулем
возникает прерывание 1 и 2, ставиться флаг 1 и 2 биты.
FLAG_REG = 0x03;
вы входите в обработчик первого прерывания
в нем пишите
FLAG_REG &= ~0x01;
это на самом деле выглядит так
1. TEMP = FLAG_REG;
2. TEMP = TEMP & 0xFE = 0x03 & 0xFE = 0x02;
3. FLAG_REG = TEMP = 0x02;

казалось бы вот оно счастье все вышло,

но что если между 1 и 2, или 2 и 3 возникло еще прерывания и FLAG_REG стал 0x07,
что в нем останется после 3? правильно, потеря третьего прерывания...


именно по этому флаги прерываний в подавляющем большинстве делают чувствительными только к одному уровню сигнала, и сбрасывают их через =, то есть через 1 тактовую операцию...






adnega
Цитата(Jenya7 @ Jul 28 2014, 18:12) *
Кстати я не вижу большого криминала обрабатывать энкодер на прерываниях.

Вы же сами жаловались, что ничего не работает! Когда CPU постоянно сидит в обработчиках прерываний mainloop не крутится и не работают менее приоритетные (и этого же уровня) прерывания - что есть зло. С учетом того, что в энкодере будет дребезг - в это время ничего работать не будет.
А начнет пользователь крутить быстро - так вообще много чудес можно словить в устройстве, с таким количеством "важной" периферии, что не остается
свободных ног таймера.
Jenya7
Golikov A.
Спасибо за подробное объяснение.

Цитата(adnega @ Jul 28 2014, 22:27) *
Вы же сами жаловались, что ничего не работает! Когда CPU постоянно сидит в обработчиках прерываний mainloop не крутится и не работают менее приоритетные (и этого же уровня) прерывания - что есть зло. С учетом того, что в энкодере будет дребезг - в это время ничего работать не будет.
А начнет пользователь крутить быстро - так вообще много чудес можно словить в устройстве, с таким количеством "важной" периферии, что не остается
свободных ног таймера.


Тут конечно все дело в частоте прерываний. У иеня максимальная 200 Герц значит 5 ms. Дьюти 50% - 2.5 ms между перепадами. Это достаточно большое время как мне кажется.
adnega
Цитата(Jenya7 @ Jul 29 2014, 10:15) *
Тут конечно все дело в частоте прерываний. У иеня максимальная 200 Герц значит 5 ms. Дьюти 50% - 2.5 ms между перепадами. Это достаточно большое время как мне кажется.

Да уж - ни туда, ни сюда.
Возможно, в этом случае опрос может проиграть EXTI.
Я бы все равно постарался подключить к таймеру.
Может, МК другой посмотреть: в F100 таймеров обычно больше (правда частота CPU до 24МГц).
Jenya7
Цитата(adnega @ Jul 29 2014, 14:30) *
Может, МК другой посмотреть: в F100 таймеров обычно больше (правда частота CPU до 24МГц).

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

Но я не могу посчитать три канала на одном таймере.
adnega
Цитата(Jenya7 @ Jul 29 2014, 14:29) *
Но я не могу посчитать три канала на одном таймере.

А двух разве не достаточно?
Jenya7
Цитата(adnega @ Jul 29 2014, 18:46) *
А двух разве не достаточно?

есть системы с тремя моторами. кстати я и два посчитать не могу - каунтер регистр ведь один.
Golikov A.
ПЛИС надо ставить...
Jenya7
Цитата(Golikov A. @ Jul 29 2014, 19:41) *
ПЛИС надо ставить...

Я кстати это предложил в беседе с моим товарищем. Но тут возникает другая проблема - сопряжение с внешним миром. Скажем мне нужен UART. Сделать UART на FPGA не проблема но имплементировать полный парсер и работу со стрингами это адский труд если вообще такое реально сделать.
Golikov A.
ага, где то полдня работыsm.gif...
у нас 5 моторами рулит ПЛИС и АРМ, АРМ - езернет со стэком и всей фигней, по 2 SSP (аналог SPI) он общается с ПЛИС.
В ПЛИС автоматы которые все делают, на протоколе даже контрольная сумма есть...
SPI много легче UART в случае плис, а протокол может быть простейшим, АДРЕС, НАПРАВЛЕНИЕ (чтение-запись), ДАННЫЕ (фиксированной длинны 32 бита), контрольная сумма...
adnega
А если вместо ПЛИС поставить мелкоту, типа STM32F0xx?
Там и таймеров - что ног не хватит. И трехфазные комплиментарные выходы с аппаратным deadtime. И возможность энкодер подключить.
Плюс всякие аналоговые компараторы для защит.
Jenya7
Цитата(Golikov A. @ Jul 30 2014, 01:02) *
ага, где то полдня работыsm.gif...
у нас 5 моторами рулит ПЛИС и АРМ, АРМ - езернет со стэком и всей фигней, по 2 SSP (аналог SPI) он общается с ПЛИС.
В ПЛИС автоматы которые все делают, на протоколе даже контрольная сумма есть...
SPI много легче UART в случае плис, а протокол может быть простейшим, АДРЕС, НАПРАВЛЕНИЕ (чтение-запись), ДАННЫЕ (фиксированной длинны 32 бита), контрольная сумма...

Может подгоните имплементацию парсера комманд на ПЛИС? sm.gif

Цитата(adnega @ Jul 30 2014, 01:23) *
А если вместо ПЛИС поставить мелкоту, типа STM32F0xx?
Там и таймеров - что ног не хватит. И трехфазные комплиментарные выходы с аппаратным deadtime. И возможность энкодер подключить.
Плюс всякие аналоговые компараторы для защит.

Это кстати очень даже рабочее решение. У меня были проекты с двумя контроллерами, так сказать parallel tasking.
Golikov A.
Цитата
Может подгоните имплементацию парсера комманд на ПЛИС

Если возьмете как есть, то есть сами будите разбираться что там и зачем сделано, легкоsm.gif
Jenya7
Цитата(Golikov A. @ Jul 30 2014, 12:19) *
Если возьмете как есть, то есть сами будите разбираться что там и зачем сделано, легкоsm.gif

Я писал под ПЛИС, нет проблем разобраться в коде. sm.gif
adnega
Цитата(Jenya7 @ Jul 30 2014, 09:20) *
Это кстати очень даже рабочее решение. У меня были проекты с двумя контроллерами, так сказать parallel tasking.

STM32F030K6T6 по $1. Можно и не один поставить при желании)

ПЛИС, может, и хорошо, но дорого. И для меня (только начавшего вникать в ПЛИС) много вопросов в синхронизации разных частей проекта -
т.е. нужно заниматься более низкоуровневым проектированием (типа, как на ASM для схем пересесть).
Golikov A.
да фигня... общий принцип перевести все входы под единый клок внутри ПЛИС и забыть об этом.

CODE

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: ***
// Engineer: ***
//
// Create Date: ***
// Design Name:
// Module Name: SPIComModule
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
// "spi_cs" and "data_rdy" signal must be in
// stable state before any "spi_clk" actions
// min 2 "clk" for "data_rdy"
// and 2 "clk" for "spi_cs"
//
//////////////////////////////////////////////////////////////////////////////////
module SPIComModule_v4_00(
clk, //основной клок

spi_cs, //сигнал выбора SPI
spi_clk, //SPI клок данных
spi_in, //входные данные SPI
spi_out, //выходные данные SPI

module_sel, //выбор модуля
submodule_sel, //выбор подмодуля
data_to_module, //данные для модуля
data_from_module, //данные от модуля

wr_rd_sync, //сигнал синхронизации чтения - записи
wr_strb, //строб записи
wr_ack, //подтверждение записи
rd_strb, //строб чтения
rd_ack, //подтверждение чтения

check_sum_ok, //сигнал подтверждения правильной контрольной суммы
data_write_done, //сигнал подтверждения записи данных
data_rdy //сигнал готовности данных для чтения
);

//================================================================================
==
parameter MODULE_DATA_SIZE = 32; //размер данных модуля
parameter MODULE_ADDR_SIZE = 4; //размер поля адреса
parameter MODULE_ADDR_VECTOR_SIZE = (32'b01 << MODULE_ADDR_SIZE); //размер вектора выбора модуля (4 бита адрес)
parameter SUB_ADDR_SIZE = 3; //размер поля подадреса
parameter SUB_ADDR_VECTOR_SIZE = (32'b01 << SUB_ADDR_SIZE); //размер вектора выбора подмодуля (3 бита подадрес)

parameter READ_DIRRECTION_VAL = 1; //значение бита при котором идет чтение

parameter ADDR_FIELD_SIZE = 8; //8 бит поле адреса
parameter CHECKSUM_FIELD_SIZE = 8; //8 бит поле контрольной суммы
localparam DATA_FIELD_SIZE = MODULE_DATA_SIZE; //размер поля данных

//размер регистра расчета длинны сообщения для определения
//окончания приема
parameter MES_LEN_COUNTER_SIZE = 6;

//размер регистра расчета длинны слова контрольной суммы
//для определения окончания приема очередного слова
parameter CHECKSUM_LEN_COUNTER_SIZE = 3;

//контрольная сумма по всем сообщению должна дать 0
//чтобы полностью нулевое сообщение не прошло как верное
//начинаем считать сумму с этого значения
parameter START_CHECKSUM_VAL = 'hA5;

//================================================================================
==
input clk;

input spi_cs;
input spi_clk;
input spi_in;
output spi_out;

output reg [MODULE_ADDR_VECTOR_SIZE - 1 : 0] module_sel = 0;
output reg [SUB_ADDR_VECTOR_SIZE - 1 : 0] submodule_sel = 0;
output reg [MODULE_DATA_SIZE - 1 : 0] data_to_module = 0;
input [MODULE_DATA_SIZE - 1 : 0] data_from_module;

//этот сигнал должен быть лишен мета-стабильности
//во внешних схемах
input wr_rd_sync;

output wr_strb;
input wr_ack;
output rd_strb;
input rd_ack;

output reg check_sum_ok = 0;
output reg data_write_done = 0;
output reg data_rdy = 0;

//================================================================================
==
//регистры для задания состояние строба чтения и записи
//эти регистры задает схема, а выходное значение получается
//с учетом сигнала синхронизации
reg wr_strb_reg = 0;
reg rd_strb_reg = 0;

//если разрешена запись или чтение, выдаем наружу стробы
assign wr_strb = (wr_strb_reg & wr_rd_sync);
assign rd_strb = (rd_strb_reg & wr_rd_sync);

//для синхронизации 2 клоковых доменов
//основного и SPI используем систему семафоров
//задание и подтверждение задания
reg need_start_recive = 0; //необходимо начать прием с 1 клоком SPI
reg need_start_recive_ack = 0; //подтверждение, прием начат

reg recive_done = 0; //окончание приема
//так как сигнал из другого клокового домена
//пропустим его через 2 триггера, это создаст
//доп задержку на стабилизацию данных
//по констрайнам частота процессора в 2 раза выше частоты SPI
//значит через 2 клока данные SPI гарантированно верны
reg recive_done_1 = 0;
reg recive_done_2 = 0;
reg recive_done_ack = 0; //подтверждение окончания приема

reg need_start_transllate = 0; //необходимо начать передачу данных
reg need_start_transllate_ack = 0; //передача данных начата


//вектор входного сообщения
localparam FULL_MESSAGE_LEN = ADDR_FIELD_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE;
reg [FULL_MESSAGE_LEN - 1 : 0] input_data_vector = 0;
//для удобства работы разделим поля входного вектора
wire [DATA_FIELD_SIZE - 1 : 0] data_w;
assign data_w = input_data_vector[DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: DATA_FIELD_SIZE];
wire [SUB_ADDR_SIZE - 1 : 0] subaddr_w;
assign subaddr_w = input_data_vector[SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: SUB_ADDR_SIZE];
wire [MODULE_ADDR_SIZE - 1 : 0 ] addr_w;
assign addr_w = input_data_vector[MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: MODULE_ADDR_SIZE];
wire read_direction_w;
assign read_direction_w = input_data_vector[1 + MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: 1];


//вектор расчета контрольной суммы
reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_calc_vector = START_CHECKSUM_VAL;
//вектор приема данных для расчета контрольной суммы
reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_vector = 0;
//вектор выходных данных, на 1 бит меньше чем данные
//так как первый отправляемый бит в него не сохраняется
reg [DATA_FIELD_SIZE - 2 : 0] output_vector = 0;
//значение выходного бита данных SPI должно быть готово
//до первого клока, еще до защелки данных в сдвиговый регистр
//поэтому первый бит берется из входного вектора
//а последующие уже из сдвигового регистра
//в начальный момент будет стоять семафор
assign spi_out = (need_start_transllate != need_start_transllate_ack) ?
data_from_module [MODULE_DATA_SIZE - 1] : //начальный момент
output_vector[MODULE_DATA_SIZE - 2]; //последующие посылки (вектор на 1 бит меньше данных)


//счетчик длинны сообщения
reg [MES_LEN_COUNTER_SIZE - 1 : 0] mes_len_counter = 0;
//счетчик длинны слова контрольной суммы
reg [CHECKSUM_LEN_COUNTER_SIZE - 1 : 0] checksum_len_counter = 0;


//================================================================================
==
// обработка основного клока
//================================================================================
==
always @(posedge clk)
begin
//-------------------- переход между клоковыми доменами
recive_done_1 <= recive_done;
recive_done_2 <= recive_done_1;


//-------------------- обмен данными
if(spi_cs == 1'b1) //если нет выбора SPI
begin
//чип селект имеет гарантированную паузу, он асинхронный
//гарантированная пауза приведет схему в правильное состояние
//выборы адреса - one hot, один бит в векторе,
//при снятии не может так получиться что бит измениться
//или возникнут еще какие-то соседние биты,
//потому даже если адрес изменится до снятия wr(rd)_strb
//так как сигнал cs асинхронный, это не страшно, другие
//адреса не выберутся и неправильных транзакций не будет
//значение битов ответов тоже не важно, если чип селект
//вверху их уже не проверяют

//подготовимся к приему следующего сообщения
//первый фронт SPI клока - начало приема
need_start_recive <= ~need_start_recive_ack;

//снимем все стробы
wr_strb_reg <= 0;
rd_strb_reg <= 0;

//снимем все выборы,
module_sel <= 0;
submodule_sel <= 0;

//снимаем все ответы
check_sum_ok <= 0;
data_write_done <= 0;
data_rdy <= 0;

//на всякий случай, если было поднятие чипселекта
//до окончания обмена, снимем флаг окончания приема
recive_done_ack <= recive_done_2;
end
else //если есть выбор SPI
begin
if(recive_done_2 != recive_done_ack) //если закончен прием
begin
//ставим подтверждение обработки флага
recive_done_ack <= recive_done_2;

//если сошлась контрольная сумма
//последние сложение надо сделать в этой процедуре
//так как после приема последнего бита
//нет больше тактов (клоков SPI) на выполнение
if((checksum_calc_vector + checksum_vector) == {CHECKSUM_FIELD_SIZE{1'b0}})
begin
check_sum_ok <= 1'b1; //ставим признак

//ставим выбор модуля и подадреса
//сразу производим частичную дешифрацию адреса
module_sel <= ({{(MODULE_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (addr_w));
submodule_sel <= ({{(SUB_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (subaddr_w));

if(read_direction_w == READ_DIRRECTION_VAL) //если режим чтения
begin
rd_strb_reg <= 1'b1; //строб чтения
//отмечаем что нужно будет начать передачу
//захват данных в регистр произойдет по
//SPI клоку, а его выдаст мастер только после
//получения сигнала готовности данных
//так что это значение можно тут выставить
//и не заботится о нем, даже если будет прерывание обмена
//без фазы чтения
need_start_transllate <= ~need_start_transllate_ack;
end
else //режим записи
begin
data_to_module <= data_w; //выставляем данные
wr_strb_reg <= 1'b1; //строб записи
end
end //контрольная сумма
end //закончен прием

//если не будет признака окончания приема
//не будет проверки контрольной суммы
//и не будет запросов на чтение или запись
//и сигналы ниже не появятся

//если есть запрос на запись и подтверждение
if((wr_strb_reg == 1'b1)&&(wr_ack == 1'b1))
data_write_done <= 1'b1; //отмечаем что данные записаны

//если есть запрос на чтение и подтверждение
if((rd_strb_reg == 1'b1)&&(rd_ack == 1'b1))
data_rdy <= 1'b1; //отмечаем что данные готовы

end //есть выбор SPI
end //конец обработки клока


//================================================================================
==
// обработка клока SPI
//================================================================================
==
//выбор SPI не проверяем, так как без выбора не
//пройдет конец обмена, весь обмен идет внутри
//одного выбора, клоков вне обмена нет
always @(posedge spi_clk)
begin

//----------------------------------------------------------------
if (need_start_recive != need_start_recive_ack) //если начало приема ы
begin
//отмечаем начало приема
need_start_recive_ack <= need_start_recive;
//сохраняем первый принятый бит
input_data_vector <= {{(FULL_MESSAGE_LEN - 1){1'b0}},spi_in};
checksum_vector <= {{(CHECKSUM_FIELD_SIZE - 1){1'b0}},spi_in};
//сохраняем начальное значение вектора расчета контрольной суммы
checksum_calc_vector <= START_CHECKSUM_VAL;

//заряжаем счетчики, первый бит уже принят,
//окончание приема всего сообщения по равенству нулю
//счетчик всего сообщения на 1 бит меньше чтобы
//он стал нулем в момент приема последнего бита
//а не после приема, вместе с приемом последнего
//бита выставиться и признак окончания приема
mes_len_counter <= (FULL_MESSAGE_LEN - 2);
checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1);

end
else //прием
begin
//уменьшаем счетчики с каждым принятым битом
mes_len_counter <= mes_len_counter - 1'b1;
checksum_len_counter <= checksum_len_counter - 1'b1;

//прием сообщения, сдвигаем входной вектор дополняя входным битом
input_data_vector <= {input_data_vector[FULL_MESSAGE_LEN - 2 : 0], spi_in};
//параллельно сохраняем вектор контрольной суммы
checksum_vector <= {checksum_vector [CHECKSUM_FIELD_SIZE - 2 : 0], spi_in};

//если приняли все сообщение
if(mes_len_counter == 0)
recive_done <= ~recive_done_ack; //отмечаем окончание приема

//если приняли очередное слово контрольной суммы
if(checksum_len_counter == 0)
begin
//заряжаем счетчик заново
checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1);
//добавляем принятый вектор в сумму
checksum_calc_vector <= checksum_calc_vector + checksum_vector;
end
end //прием

//-----------------------------------------------------------------
//значение передаваемых данных во время приема не важны,
//потому всегда передаем, к фазе передачи данные будут
//запрошены и корректно обновлены
if (need_start_transllate != need_start_transllate_ack) //если начало передачи
begin
//отмечаем начало передачи
need_start_transllate_ack <= need_start_transllate;
//сохраняем данные от модуля без старшего бита,
//так как первый бит уже передан (взят из данных на отправку)
//после этого такта, на выход будет передаваться старший бит
//этого вектора
output_vector <= data_from_module[DATA_FIELD_SIZE - 2 : 0];
end
else //передача
begin
//просто сдвигаем вектор данных
//значение дополнения не важно
output_vector <= (output_vector << 1);
end //передача

end //конец обработки клока SPI

endmodule



wr_rd_sync - этот сигнал в 1 зажмите, он для синхронизации служит, это для нашей специфики было нужно.
Jenya7
Цитата(adnega @ Jul 30 2014, 12:35) *
STM32F030K6T6 по $1. Можно и не один поставить при желании)

ПЛИС, может, и хорошо, но дорого. И для меня (только начавшего вникать в ПЛИС) много вопросов в синхронизации разных частей проекта -
т.е. нужно заниматься более низкоуровневым проектированием (типа, как на ASM для схем пересесть).


ПЛИС очень сильная штука. Во первых real time действительно real - несколько процессов могут бежать параллельно. Во вторых полная свобода выбора ног - куда посадить рвм, куда энкодер - соответственно разводка платы существенно оптимизируется. Кроме того как говорят надежней в работе. Ну конечно пересесть с С на VHDL тут придется немного попотеть.


Golikov A.,
Большое спасибо !
Golikov A.
Огромный бонус ПЛИС - это возможность создание реально параллельных блоков, а еще в добавок там куча памяти теперь и прочих радостей. Можно и проц запихать, отказавшись от внешнего. Для частной маленькой задачи обработки энкодеров можно взять малюсенькую CPLD.

Я вот тут пытался представить как обработать энкодер на таймере, у меня не получилось 4 электронных отсчетов на реальную линию, и направление надо учитывать, а в ПЛИС все будет четко, 4 электронных отсчета на реальный, и регистры накопления внутри ПЛИС.

да еще забыл

spi_clk - вот этот сигнал лучше завести на клоковый вход ПЛИС (это спец ножки, их много). Можно и на обычный, но времянка просядет, на клоковый лучше.

И я вам настоятельно рекомендую пересаживаться на Verilog, я начинал с VHDL, а теперь на верилог пересел, одни положительные эмоции

во я еще тестбенч нашел от модуляsm.gif

CODE

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 15:18:31 04/16/2014
// Design Name: SPIComModule_v4_00
// Module Name: E:/SYNC_MOTOR/FPGA/SPIComModule_v4_00_tb.v
// Project Name: SyncMotor
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: SPIComModule_v4_00
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////

module SPIComModule_v4_00_tb;
localparam DATA_SIZE = 16;

// Inputs
reg clk;
reg spi_cs;
reg spi_clk;
reg spi_in;
reg [DATA_SIZE - 1 : 0] data_from_module;
reg wr_rd_sync;
reg wr_ack;
reg rd_ack;

// Outputs
wire spi_out;
wire [15:0] module_sel;
wire [7:0] submodule_sel;
wire [DATA_SIZE - 1 : 0] data_to_module;
wire wr_strb;
wire rd_strb;
wire check_sum_ok;
wire data_write_done;
wire data_rdy;

reg [DATA_SIZE - 1 : 0] datareg_00;
reg [DATA_SIZE - 1 : 0] datareg_01;
reg [DATA_SIZE - 1 : 0] datareg_10;
reg [DATA_SIZE - 1 : 0] datareg_11;

reg [DATA_SIZE - 1 : 0] data ;
reg [7 : 0] addr ;
reg [7 : 0] check_sum;

// Instantiate the Unit Under Test (UUT)
SPIComModule_v4_00
#(.MODULE_DATA_SIZE(DATA_SIZE)
)
uut
(
.clk(clk),
.spi_cs(spi_cs),
.spi_clk(spi_clk),
.spi_in(spi_in),
.spi_out(spi_out),
.module_sel(module_sel),
.submodule_sel(submodule_sel),
.data_to_module(data_to_module),
.data_from_module(data_from_module),
.wr_rd_sync(wr_rd_sync),
.wr_strb(wr_strb),
.wr_ack(wr_ack),
.rd_strb(rd_strb),
.rd_ack(rd_ack),
.check_sum_ok(check_sum_ok),
.data_write_done(data_write_done),
.data_rdy(data_rdy)
);

integer i;

initial begin
// Initialize Inputs
spi_cs = 0;
spi_clk = 0;
spi_in = 0;
wr_rd_sync = 0;
data = 0;
addr = 0;
check_sum = 0;

// Wait 100 ns for global reset to finish
#100;

// Add stimulus here
spi_cs <= 1;
spi_clk <= 1;

#100;


//-----------------------------------------------------------
data = 16'h0000;
addr = 8'b10010001;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 0;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;
wr_rd_sync = 1;
#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------






//-----------------------------------------------------------
#50;
data = 16'hAABB;
addr = 8'b00001001;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------




//-----------------------------------------------------------
#50;
data = 16'hCCDD;
addr = 8'b00010010;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------


//-----------------------------------------------------------
#50;
data = 16'hAABB;
addr = 8'b10001001;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------

//-----------------------------------------------------------
#50;
data = 16'hAABB;
addr = 8'b10010010;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------



//-----------------------------------------------------------
#50;
data = 16'hEEFF;
addr = 8'b00000000;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------

//-----------------------------------------------------------
#50;
data = 16'h0000;
addr = 8'b10000000;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 1;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------


#1000;



//-----------------------------------------------------------
#50;
data = 16'hCCCC;
addr = 8'b10010010;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 0;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------



//-----------------------------------------------------------
#50;
data = 16'hCCCC;
addr = 8'b00000000;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 0;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------

//-----------------------------------------------------------
#50;
data = 16'h0000;
addr = 8'b10000000;
check_sum = 8'hFF + data[7:0] + data[15:8] + addr;
check_sum = 0 - check_sum;

wr_rd_sync = 0;
spi_cs <= 0;

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = addr[7-i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = data[DATA_SIZE - 1 -i];
#2;
spi_clk = 1;
#10;
end

for (i=0;i<8;i=i+1)
begin
spi_clk = 0;
#8;
spi_in = check_sum[7-i];
#2;
spi_clk = 1;
#10;
end

#100;

if(data_rdy == 1)
for (i=0;i<DATA_SIZE;i=i+1)
begin
spi_clk = 0;
#10;
spi_clk = 1;
#10;
end

spi_cs <= 1;
//-----------------------------------------------------------

end


initial begin
// Initialize Inputs
clk = 0;

// Wait 100 ns for global reset to finish
#98;

// Add stimulus here
forever begin
#5;
clk <= ~clk;
end

end

initial begin
// Initialize Inputs
datareg_00 = 0;
datareg_01 = 0;
datareg_10 = 0;
datareg_11 = 0;
wr_ack = 0;
rd_ack = 0;
data_from_module = 0;

// Wait 100 ns for global reset to finish
#100;

// Add stimulus here
forever begin
#1;
wr_ack <= 0;
rd_ack <= 0;
data_from_module <= 'hFFFF;

if((module_sel == 2'b1)&&(submodule_sel == 2'b1))
begin
if(wr_strb == 1)
begin
datareg_00 <= data_to_module;
wr_ack <= 1;
end
if(rd_strb == 1)
begin
data_from_module <= datareg_00;
rd_ack <= 1;
end
end

if((module_sel == 2'b10)&&(submodule_sel == 2'b1))
begin
if(wr_strb == 1)
begin
datareg_10 <= data_to_module;
wr_ack <= 1;
end
if(rd_strb == 1)
begin
data_from_module <= datareg_10;
rd_ack <= 1;
end
end


if((module_sel == 2'b1)&&(submodule_sel == 2'b10))
begin
if(wr_strb == 1)
begin
datareg_01 <= data_to_module;
wr_ack <= 1;
end
if(rd_strb == 1)
begin
data_from_module <= datareg_01;
rd_ack <= 1;
end
end

if((module_sel == 2'b10)&&(submodule_sel == 2'b10))
begin
if(wr_strb == 1)
begin
datareg_11 <= data_to_module;
wr_ack <= 1;
end
if(rd_strb == 1)
begin
data_from_module <= datareg_11;
rd_ack <= 1;
end
end

end

end


endmodule


Jenya7
Перед тем как начать програмироать на ПЛИС я читал много статей VHDL vs Verilog - все таки общий тренд в пользу VHDL.
DmitryM
Цитата(Jenya7 @ Jul 30 2014, 11:30) *
Перед тем как начать програмироать на ПЛИС я читал много статей VHDL vs Verilog - все таки общий тренд в пользу VHDL.

Добавлю еше что VHDL ГОСТирован, а Verilog - нет. общий тренд в пользу VHDL.
Golikov A.
Поскольку мне надо делать дело, а не быть в тренде я выбираю Verilog sm.gif
Это священная война, и вы можете выбирать любую сторону.

Я выбрал верилог по причине того что писать на нем легче, меньше букв, меньше атавистических библиотек которые перекрывают друг друга вызывая путаницы и несостыковки проекта. VHDL - старинный монстр, который несет на себе груз не только работы с ПЛИС, но и общего описания железа, в нем можно описать все что хочешь и максимально строго, плата за это что там надо топтать клавиатуру не по детски, и самая простая схема требует много букв. Если же ограничиться только работой с ПЛИС, то верилога 100% хватит, и писать будет удобнее. Общая тенденция что плисовики знают оба языка, потому что они представленны одинаково на рынке, а с какого начать вам решать)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.