Hola,
Estoy estudiando y tratando de entender el código Verilog del
bloque de bombeo a 1Hz con ciclo de trabajo del 50% en la
colección "Academia-Jedi-HW-06". He hecho la prueba con
"localparam M= 6", para obtener un divisor por 6 de una señal de
reloj que genero en el testbench. Un divisor por 6 puedo
gestionarlo más fácilmente.
El código que he probado es "divisor_50.v":
idéntico al del bloque corazón. El testbench que he usado es "divisor_50_tb.v":
Esperaba tener un divisor x6 de la señal de reloj clk con un
ciclo de trabajo del 50%, pero lo que sale es un divisor x8, tal y
como se ve en la captura 1 que os adjunto.
He cambiado el código para que en vez de contar hasta M/2 se cuente hasta M/2 -1, y en el primer bloque "always" también sustiuyo "clk_t= 0" por "clk_t <= 0". El nuevo código "divisor_50.v" queda como:
y con este sí que la simulación da una señal "clk_o" que es un divisor x6 de "clk", al 50%, como se observa en la captura 2, que también os adjunto. Si no se sustiuye "clk_t= 0" por "clk_t <= 0" la cosa no funciona. Tengo entendido que lo habitual es poner hacer una asignación "non-blocking" en los bloques "always", pero no acabo de entender la sutileza de que después de cambiar de "M/2" a "M/2 -1" si no se hace asignación con "non-blocking" la cosa no rule como se quiere.
Por favor, confirmarme si estoy en lo cierto y si entendéis la
necesidad de la asignación "non-blocking" para "clk_t".
Saludos y muchas gracias,
Jorge
Hola Demócrito,
Muchísimas gracias por tu contestación y por responder tan
rápidamente.
El principal punto que quería comentar en el mail anterior, es
que en mi opinión, para conseguir el divisor de frecuencia por M
con duty 50%, hay que sumar a "divcounter" hasta M/2 -1, y no M/2.
Me gustaría que me confirmarais ese punto. Además, como divcounter
sólo llega a ser usado hasta M/2 -1 se podría calcular el
parámetro N como $clog2(M/2-1) y así tenemos "divcounter" con una
dimensión más ajustada y quizá se ahorra algún recurso en la fpga.
Por otra parte, y comentando las explicaciones que me das:
1. Por lo que sé de Verilog, una variable se define como "wire" o "reg" dependiendo de si aparece en un bloque "always" o no. Si aparece en un always es "reg" y si no aparece es un "wire". Nada que ver con que la variable esté relacionada con un DFF o sea pura combinacional. Se puede hacer código totalmente combinacional y usar reg, por ejemplo:
```
module full adder
(input a,b,cin,
output sum,cout);
reg sum;
reg cout;
always@(a or b or cin) //siempre que haya un cambio en
cualquiera de ellas
begin
sum = a^b^cin;
cout=(a&b) | (a&cin) | (b&cin);
end
endmodule
```
Como "sum" y "cout" se utilizan en un bloque always, se tienen que definir como "reg" aunque todo sea puramente combinacional. El full-adder se podría escribir sin necesidad de un always, cierto, pero a veces utilizar always permite introducir condicionales o hacer operaciones que justifica usar un "always", y por tanto las variables que están dentro, y no son las de entrada, tienen que ser reg.
Mis referencias de verilog es el curso de obijuan y el curso http://web.mit.edu/6.111/www/f2016/ (adjunto las transparencias relacionadas con verilog directamente y de donde he sacado el ejemplo anterior). En la transparencia 23 del L03.pdf se comenta el tema reg/wire.
2. Estoy de acuerdo contigo en que dentro de los dos always del bombeo se tiene que usar "<=", pero sólo comentarte que el fichero que he puesto en el mail anterior es exactamente el que se utiliza en el corazón de 1Hz. De hecho he añadido "reg[N-1:0] divcounter = 0;". En el original aparece "reg[N-1:0] divcounter;" y al hacer la simulación con iverilog, la variable "divcounter" no tomaba valores.
Para hacer las simulaciones desde línea de comando utilizo:
```
$ iverilog -o divisor_50_tb.out divisor_50.v divisor_50_tb.v <---- requiere: reg[N-1:0] divcounter = 0;
```
3. Supongo que lo conocéis todos, pero existe una herramienta en
yosys que permite ver el diagrama de bloques a partir del código
verilog. Creas un fichero "show_rtl.ys" :
```
# read design, en mi caso divisor_50.v
read_verilog divisor_50.v
hierarchy -check
# high-level synthesis
proc; opt; fsm; opt; memory; opt
# se genera el fichero divisr_50.ps en el mismo directorio de
trabajo
show -format ps -prefix ./divisor_50
```
y ejecutas:
$ yosys show_rtl.ys
Se genera un fichero postcript divisor_50.ps que puedes visulizar con "gv" o cualquier lector de postscript.
En línea de comando:
$ gv divisor_50.ps
Os adjunto el resultado en el fichero Caputra RTL.png
Lo curioso, es que utilices asignaciones bloqueantes o no, el
diagrama RTL que genera yosys sale siempre el mismo, el de la
captura, sin embargo, al simular el testbench con iverilog en unos
casos aparece correcto el funcionamiento como divisor por 6 y en
otros no, según el tipo de asignación que se haga.
El resultado gráfico del diagrama RTL es un poco espeluznante,
sin duda, pero si borras las líneas de clk y te lo redibujas a mi
me sirve para aprender mucho de cómo se convierte en bloques el
código verilog que escribo. Si alguno conocéis alguna otra
herramienta que haga eso, por favor, comentármelo. Supongo que las
herramientas propias de Lattice para las ice40 lo permitirán, pero
no las he probado y no sé si son gratuitas. ¿Alguién las ha
probado?
En cualquier caso, puestos a pedir, me encantaría que Icestudio
tuviera la posibilidad de escribir verilog y que lo convirtiera en
bloques RTL el solito, como hace la herramienta de yosys. Que el
resultado sea más amigable será fácil :-).
Contar conmigo en lo que pueda ayudar.
4. En el código tengo puesto M=6 pues quiero hacer un divisor por
6. El reloj de entrada no tiene porqué ser el de 12MHz. Si el
reloj de entrada es de 12MHz y quiero un reloj de 1Hz, tengo que
usar un divisor por 12 millones, claro, por eso M=12_000_000 en el
código original. En el módulo, el reloj será el que se le ponga
por su puerto de entrada. Al hacer la simulación con el testbench
le pongo un reloj con periodo dos unidades de tiempo de la
simulación, que por defecto son segundos, es decir el periodo del
reloj de entrada de la simulación es de 2 sg.
He usado M=6 porque me permite fácilmente contar los periodos del divisor en gtkwave y ver si funciona correctamente.
5. Si te he entendido bien, el circuito que propones es para
"limpiar" una señal de reloj y hacerla 50% duty, eso no es
exactamente lo que buscaba, pero muchas gracias de todas formas
por la contestación y el circuito.
Saludos,
Jorge
--
Has recibido este mensaje porque estás suscrito al grupo "FPGAwars: explorando el lado libre" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a fpga-wars-explorando-el...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/fpga-wars-explorando-el-lado-libre/c1d644a2-5f84-4e4d-a58b-905ad86fad94n%40googlegroups.com.