Sending SPI-style serial data to DAC on register change

N

Nicholas Kinar

Guest
Hello--

I've designed a custom circuit board with a Cyclone II FPGA, and I am
now writing Verilog code to send a control word to a 16-bit DAC. The
DAC is a Texas Instruments/Burr-Brown part (DAC8580, datasheet available
at http://www.ti.com/lit/gpn/dac8580) which communicates via an
SPI-style interface with the chip-select line (FSYNC) active high. A
control word is latched into the DAC on the rising edge of the SPI bus
clock.

The system clock for the FPGA is 30MHz, and the maximum frequency of the
SPI clock for the DAC is also 30MHz.

I am using the i2cslave core from OpenCores.org to set up I2C
communications between a 32-bit DSP running Linux and the Cyclone II
FPGA. The i2cslave core (which is written in Verilog) works quite well.
I have written a Linux kernel module to read and write I2C registers
provided by the i2cslave core.

The FPGA serves as the I2C slave. I have defined three I2C registers,
each 8 bits in length.

(1) msb_data (Contains the MSB of the 16-bit control word)
(2) lsb_data (Contains the LSB of the 16-bit control word)
(3) load_word (Writing to this register will send the 16-bit control
word to the DAC)


The idea is for the user to use the I2C bus to load the msb_data
register and the lsb_data register with the MSB and LSB of the control
word. The user then writes a value to the load_word register. When a
value is written to the load_word register, the contents of the msb_data
and lsb_data registers are sent to the DAC. After the 16-bit control
word is sent to the DAC, the load_word register is reset to zero.

I've written Verilog code to send a control word to the DAC, but it
appears that the data is not latched into the device when I use I2C to
write a value to the load_word register. An LED is attached to one of
the port pins of the FPGA.

I have compiled the Verilog code with Quartus II Web Edition 9.1 Linux
Beta. (The Linux beta currently works quite well, but can crash when
some dialog boxes are open.)

What is happening with my code? What would I have to do to shift out a
control word to the DAC when the user writes a value to the load_word
register?

Here is the module that I have written to attempt to communicate with
the DAC:


module dac( clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
sclk_dac,
fsync_dac,
led);

input clk;
inout load_word;


input [7:0] msb_data;
input [7:0] lsb_data;

output sdin_dac;
output sclk_dac;
output fsync_dac;

output led;

reg [15:0] data;
reg count;

// Clock to DAC is continuous
assign sclk_dac = clk;

// Data is the concatenation of two registers
assign data = {msb_data, lsb_data};


// The data is latched in on the rising edge of the clock
// so the setup needs to be done on the falling edge?
always @(negedge clk) begin

if(load_word == 1'b1) begin

led <= 1'b1; // why is the led on when load_word is low?

if(count == 1'b0) begin
fsync_dac <= 1'b1; // CS is set high
end

if(count < 16) begin
// clock the data out MSB first
sdin_dac = data[15 - count];
count = (count + 1'b1);
end

if(count == 16) begin
count <= 0;
fsync_dac <= 0; // lower CS
load_word <= 0; // reset load_word
end

end // load_word block

end // clk always


endmodule
 
Nicholas Kinar wrote:
snip
Here is the module that I have written to attempt to communicate with
the DAC:
See comments inline.
You should increase the size of count. Try single stepping the code.
Good luck Andy.
module dac( clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
sclk_dac,
fsync_dac,
led);

input clk;
inout load_word;


input [7:0] msb_data;
input [7:0] lsb_data;

output sdin_dac;
output sclk_dac;
output fsync_dac;

output led;

reg [15:0] data;
reg count;
Only one bit's worth of data.

// Clock to DAC is continuous
assign sclk_dac = clk;

// Data is the concatenation of two registers
assign data = {msb_data, lsb_data};


// The data is latched in on the rising edge of the clock
// so the setup needs to be done on the falling edge?
always @(negedge clk) begin

if(load_word == 1'b1) begin

led <= 1'b1; // why is the led on when load_word is low?

if(count == 1'b0) begin
fsync_dac <= 1'b1; // CS is set high
end

if(count < 16) begin
Comparing with 5 bits of data.
// clock the data out MSB first
sdin_dac = data[15 - count];
count = (count + 1'b1);
Incrementing.
end

if(count == 16) begin
count <= 0;
fsync_dac <= 0; // lower CS
load_word <= 0; // reset load_word
end

end // load_word block

end // clk always


endmodule
 
Hello Andy--


Thank you so much for your response! I think that I see what you are
pointing out to me.

So perhaps I should do the following:

reg count; // change this
reg [4:0] count; // to this


Now this appears to ensure that count can hold the total.




Andy Botterill wrote:

[snip]

data;
reg count;
Only one bit's worth of data.
[snip]

if(count < 16) begin
Comparing with 5 bits of data.
// clock the data out MSB first
sdin_dac = data[15 - count];
count = (count + 1'b1);
Incrementing.
 
Okay, I've made the change from "reg count" to "reg [4:0] count."

After attempting to compile with Quartus II, there's another error which
occurs. Here's the error:

Error: Net "load_word", which fans out to "dac:MyDac|load_word", cannot
be assigned more than one value
Error: Net is fed by "i2cSlave:u_i2cSlave|load_word[0]"
Error: Net is fed by "dac:MyDac|load_word"
Error: Net is fed by "dac:MyDac|load_word~reg0"


What does this error mean, and has anyone seen anything similar?



Nicholas Kinar wrote:
Hello Andy--


Thank you so much for your response! I think that I see what you are
pointing out to me.

So perhaps I should do the following:

reg count; // change this
reg [4:0] count; // to this


Now this appears to ensure that count can hold the total.
 
The offending area of code appears to be the access to load_word in the
following block:


if(count == 16) begin
count <= 1'b0;
fsync_dac <= 1'b0; // lower CS
load_word <= 1'b0; // if this is commented out, the error goes away
end



Nicholas Kinar wrote:
count."

After attempting to compile with Quartus II, there's another error which
occurs. Here's the error:

Error: Net "load_word", which fans out to "dac:MyDac|load_word", cannot
be assigned more than one value
Error: Net is fed by "i2cSlave:u_i2cSlave|load_word[0]"
Error: Net is fed by "dac:MyDac|load_word"
Error: Net is fed by "dac:MyDac|load_word~reg0"


What does this error mean, and has anyone seen anything similar?
 
Thank you so much for your response, Gabor! What I will do is use a
reset_lw output from my module to let the i2cslave logic know that the
load_word register must be reset.

Once again, thank you!

Nicholas



You can't use a module inout as a register. If the signal has
an external driver (i.e. when it is an input or inout) it can
only be treated as a wire. The Error is telling you that there
is more than one logic source driving net load_word. I would
suggest making a separate module output, perhaps called "reset_lw"
that tells the driving logic to reset the load_word signal. Then
load_word doesn't need to be bidirectional. If load_word is
a top level port (i.e. bidirectional pin) you should only
drive it in one place, preferably in the top level module.

HTH,
Gabor
 
On Nov 24, 2:28 pm, Nicholas Kinar <n.ki...@usask.ca> wrote:
The offending area of code appears to be the access to load_word in the
following block:

if(count == 16) begin
    count <= 1'b0;
    fsync_dac <= 1'b0;  // lower CS
    load_word <= 1'b0;  // if this is commented out, the error goes away
end

Nicholas Kinar wrote:
Okay, I've made the change from "reg count" to "reg [4:0] count."

After attempting to compile with Quartus II, there's another error which
occurs.  Here's the error:

Error: Net "load_word", which fans out to "dac:MyDac|load_word", cannot
be assigned more than one value
    Error: Net is fed by "i2cSlave:u_i2cSlave|load_word[0]"
    Error: Net is fed by "dac:MyDac|load_word"
    Error: Net is fed by "dac:MyDac|load_word~reg0"

What does this error mean, and has anyone seen anything similar?
You can't use a module inout as a register. If the signal has
an external driver (i.e. when it is an input or inout) it can
only be treated as a wire. The Error is telling you that there
is more than one logic source driving net load_word. I would
suggest making a separate module output, perhaps called "reset_lw"
that tells the driving logic to reset the load_word signal. Then
load_word doesn't need to be bidirectional. If load_word is
a top level port (i.e. bidirectional pin) you should only
drive it in one place, preferably in the top level module.

HTH,
Gabor
 
You can't use a module inout as a register. If the signal has
an external driver (i.e. when it is an input or inout) it can
only be treated as a wire. The Error is telling you that there
is more than one logic source driving net load_word. I would
suggest making a separate module output, perhaps called "reset_lw"
that tells the driving logic to reset the load_word signal. Then
load_word doesn't need to be bidirectional. If load_word is
a top level port (i.e. bidirectional pin) you should only
drive it in one place, preferably in the top level module.

HTH,
Gabor

I made the change in my Verilog code. Now reset_lw is an output from
the dac module. But I've now encountered another problem. How do I
change reset_lw back to the value which tells the driving logic to NOT
reset the load_word signal? Here is the code in the module that drives
the dac:

module
dac(clk,load_word,msb_data,lsb_data,sdin_dac,sclk_dac,fsync_dac,led,reset_lw);

[...]

output reset_lw;

[...]

always @(negedge clk) begin
if (load_word == 1) begin

led <= 1'b1;

if(count == 0) begin
fsync_dac <= 1; // CS is set high
end

if(count < 16) begin
// clock the data out MSB first
sdin_dac = data[15 - count];
count = (count + 1'b1);
end

if(count == 16) begin
count <= 0;
fsync_dac <= 0; // lower CS
reset_lw <= 1; // send the reset_lw signal
end

end // if load_word block

end // clk always

endmodule


Alternately, here is the code in module registerInterface which performs
the reset of the load_register:

always @(posedge clk) begin

if (writeEn == 1'b1) begin
case (addr)
8'h00: source_sig <= dataIn;
8'h01: sample_rate <= dataIn;
8'h02: seq_length <= dataIn;
8'h03: low_freq <= dataIn;
8'h04: high_freq <= dataIn;
8'h05: take_measurement_sp <= dataIn;
8'h06: take_measurement_dnsp <= dataIn;
8'h08: reset <= dataIn;
8'h09: take_single_measurement <= dataIn;
8'h10: load_word <= dataIn;
8'h11: led_state <= dataIn;
8'h12: msb_data <= dataIn;
8'h13: lsb_data <= dataIn;
endcase
end

// reset the load_word
if (reset_lw == 1'b1) begin
load_word <= 8'b0;
end

end // end of writeEn


But how do I assign the following?

reset_lw <= 1'b0;

What I would like to do is set reset_lw to equal 1'b0 only after
load_word <= 8'b0, but it appears that I can't do the following, since I
receive a similar error message concerning multiple logic sources:


// reset the load_word
if (reset_lw == 1'b1) begin
load_word <= 8'b0;
reset_lw <= 1'b0;
end


Here's the error message:

Error: Net "reset_lw", which fans out to "i2cSlave:u_i2cSlave|reset_lw",
cannot be assigned more than one value
Error: Net is fed by "dac:MyDac|reset_lw"
Error: Net is fed by "i2cSlave:u_i2cSlave|reset_lw"
Error: Net is fed by
"i2cSlave:u_i2cSlave|registerInterface:u_registerInterface|reset_lw"
Error: Net is fed by
"i2cSlave:u_i2cSlave|registerInterface:u_registerInterface|reset_lw~reg0"


This appears to be similar to the philosophical chicken and the egg
problem. How do I reset the reset_lw register to 1'b0 only after I have
set the load_word register to 8'b0?


Nicholas
 
On Nov 24, 10:09 pm, Nicholas Kinar <n.ki...@usask.ca> wrote:
 
 
 >> You can't use a module inout as a register.  If the signal has
 >> an external driver (i.e. when it is an input or inout) it can
 >> only be treated as a wire.  The Error is telling you that there
 >> is more than one logic source driving net load_word.  I would
 >> suggest making a separate module output, perhaps called "reset_lw"
 >> that tells the driving logic to reset the load_word signal.  Then
 >> load_word doesn't need to be bidirectional.  If load_word is
 >> a top level port (i.e. bidirectional pin) you should only
 >> drive it in one place, preferably in the top level module.
 
 >> HTH,
 >> Gabor

I made the change in my Verilog code.  Now reset_lw is an output from
the dac module. But I've now encountered another problem.  How do I
change reset_lw back to the value which tells the driving logic to NOT
reset the load_word signal?  Here is the code in the module that drives
the dac:

module
dac(clk,load_word,msb_data,lsb_data,sdin_dac,sclk_dac,fsync_dac,led,reset_lw);

[...]

output reset_lw;

[...]

always @(negedge clk) begin
right here you insert the default values for your
register that wants to go back to zero. If you want to
use feedback, remember that there is still a clock
delay after sensing the feedback condition:

reset_lw <= 0;

Simplest method, makes reset_lw into a pulse without any feedback.
Whenever the logic below is not triggered, reset_lw returns to zero.

OR

if (load_word == 0) reset_lw <= 0;

This de-asserts reset_lw on the negedge of clk after load_word
goes to zero.

        if (load_word == 1) begin

                led <= 1'b1;

                if(count == 0) begin
                        fsync_dac <= 1;  // CS is set high
                end

                if(count < 16) begin
                        // clock the data out MSB first
                        sdin_dac = data[15 - count];
                        count = (count + 1'b1);
                end

                if(count == 16) begin
                        count <= 0;
                        fsync_dac <= 0;  // lower CS
                        reset_lw <= 1;   // send the reset_lw signal
                end

        end // if load_word block      

end // clk always

endmodule

Alternately, here is the code in module registerInterface which performs
the reset of the load_register:

always @(posedge clk) begin

   if (writeEn == 1'b1) begin
     case (addr)
       8'h00:  source_sig <= dataIn;
       8'h01:   sample_rate     <= dataIn;
       8'h02:   seq_length <= dataIn;
       8'h03:   low_freq <= dataIn;
       8'h04:   high_freq <= dataIn;
       8'h05:   take_measurement_sp <= dataIn;
       8'h06:   take_measurement_dnsp <= dataIn;
       8'h08:   reset <= dataIn;
       8'h09:  take_single_measurement <= dataIn;
       8'h10:  load_word <= dataIn;
       8'h11:  led_state <= dataIn;
       8'h12:  msb_data <= dataIn;
       8'h13:  lsb_data <= dataIn;
     endcase
    end

    // reset the load_word
    if (reset_lw == 1'b1) begin
       load_word <= 8'b0;
    end

  end // end of writeEn

But how do I assign the following?

reset_lw <= 1'b0;

What I would like to do is set reset_lw to equal 1'b0 only after
load_word <= 8'b0, but it appears that I can't do the following, since I
receive a similar error message concerning multiple logic sources:

// reset the load_word
if (reset_lw == 1'b1) begin
        load_word <= 8'b0;
        reset_lw <= 1'b0;
end

Here's the error message:

Error: Net "reset_lw", which fans out to "i2cSlave:u_i2cSlave|reset_lw",
cannot be assigned more than one value
        Error: Net is fed by "dac:MyDac|reset_lw"
        Error: Net is fed by "i2cSlave:u_i2cSlave|reset_lw"
        Error: Net is fed by
"i2cSlave:u_i2cSlave|registerInterface:u_registerInterface|reset_lw"
        Error: Net is fed by
"i2cSlave:u_i2cSlave|registerInterface:u_registerInterface|reset_lw~reg0"

This appears to be similar to the philosophical chicken and the egg
problem.  How do I reset the reset_lw register to 1'b0 only after I have
set the load_word register to 8'b0?

Nicholas
 
Hello gabor--

Thank you once again for your reply! I was wondering how it might be
possible to reset the register, and this provides a nice solution.

always @(negedge clk) begin

right here you insert the default values for your
register that wants to go back to zero. If you want to
use feedback, remember that there is still a clock
delay after sensing the feedback condition:

reset_lw <= 0;

Simplest method, makes reset_lw into a pulse without any feedback.
Whenever the logic below is not triggered, reset_lw returns to zero.

OR

if (load_word == 0) reset_lw <= 0;

This de-asserts reset_lw on the negedge of clk after load_word
goes to zero.

The code has now morphed into the following form. In the most recent
version, I update count on the posedge of clk. This eliminates the need
to mix = and <= operators in the same always block.


module
dac(clk,load_word,msb_data,lsb_data,sdin_dac,sclk_dac,fsync_dac,led,reset_lw);

[...]

output reset_lw;

[...]


// reset count
always @(posedge clk) begin
if ( (load_word == 1) && (count < 16) ) count <= count + 1'b1;
if (count == 16) count <= 0;
if (load_word == 0) count <= 0;
end


always @(negedge clk) begin
if (load_word == 8'b1) begin

led <= 1'b1;

// CS is set high
if(count == 0) fsync_dac <= 1;

// shift out the data
if(count < 16) sdin_dac <= data[15 - count];

if(count == 16) begin
fsync_dac <= 0; // lower CS
reset_lw <= 0; // reset the load_word
end
end // if load_word block

// change reset_lw back again to zero
if (load_word == 0) reset_lw <= 0;

end // clk always

endmodule


Attached to my PCB is an LED, which normally should be off when led <=
1'b0. However, the LED should be on when led <= 1'b1. In the above
code, I believe that this should occur when load_word == 8'b1.

But why is the LED on when I transfer the design to my hardware via JTAG?

When I read the register load_word via I2C, I've determined that
load_word = 0 on startup. When I write "1" to load_word via I2C, the
register changes state to load_word = 1.

Shouldn't the LED turn on when load_word = 1? However, the LED is
always on. It appears that load_word == 8'b1 is true on startup.

Why should load_word == 8'b1 on startup? Shouldn't load_word == 0 on
startup?


Nicholas
 
Hello Gabor--

Once again, thank you so much for your reply!

I imagine load_word is 0 on start-up. Where do you ever set
led to zero? I only see one line with "led <= 1'b1;"
With no other assignments, synthesis could very well
optimize this to a constant 1.

Regards,
Gabor
Yes, you are right about there being no other assignments to "led," and
it is probably the case that synthesis would optimize this. So "led"
remains high. Where could I update my code to ensure that led = 0 at
startup?

Perhaps I could update this block:

always @(posedge clk) begin
if ( (load_word == 1) && (count < 16) ) count <= count + 1'b1;
if (count == 16) count <= 0;
if (load_word == 0) begin
count <= 0;
led <= 0;
end
end


However, when I try to place "led <= 0" in this block, I obtain another
synthesis error complaining about "multiple drivers."

What is the "best practice" to ensure state of "led" at startup? Is the
initial keyword synthesizable in some manner?

Many thanks--

Nicholas
 
On Nov 25, 10:53 am, Nicholas Kinar <n.ki...@usask.ca> wrote:
Hello gabor--

Thank you once again for your reply! I was wondering how it might be
possible to reset the register, and this provides a nice solution.



always @(negedge clk) begin

right here you insert the default values for your
register that wants to go back to zero.  If you want to
use feedback, remember that there is still a clock
delay after sensing the feedback condition:

          reset_lw <= 0;

Simplest method, makes reset_lw into a pulse without any feedback.
Whenever the logic below is not triggered, reset_lw returns to zero.

OR

          if (load_word == 0) reset_lw <= 0;

This de-asserts reset_lw on the negedge of clk after load_word
goes to zero.

The code has now morphed into the following form.  In the most recent
version, I update count on the posedge of clk.  This eliminates the need
to mix = and <= operators in the same always block.

module
dac(clk,load_word,msb_data,lsb_data,sdin_dac,sclk_dac,fsync_dac,led,reset_lw);

[...]

output reset_lw;

[...]

// reset count
always @(posedge clk) begin
        if ( (load_word == 1) && (count < 16) ) count <= count + 1'b1;
        if (count == 16) count <= 0;
        if (load_word == 0) count <= 0;
end

always @(negedge clk) begin
        if (load_word == 8'b1) begin

                led <= 1'b1;

                // CS is set high
                if(count == 0) fsync_dac <= 1;

                // shift out the data
                if(count < 16) sdin_dac <= data[15 - count];

                if(count == 16) begin
                        fsync_dac <= 0;  // lower CS
                        reset_lw <= 0;    // reset the load_word
                end
        end // if load_word block      

        // change reset_lw back again to zero
        if (load_word == 0) reset_lw <= 0;

        end // clk always

endmodule

Attached to my PCB is an LED, which normally should be off when led <> 1'b0. However, the LED should be on when led <= 1'b1.  In the above
code, I believe that this should occur when load_word == 8'b1.

But why is the LED on when I transfer the design to my hardware via JTAG?

When I read the register load_word via I2C, I've determined that
load_word = 0 on startup.  When I write "1" to load_word via I2C, the
register changes state to load_word = 1.

Shouldn't the LED turn on when load_word = 1?  However, the LED is
always on.  It appears that load_word == 8'b1 is true on startup.

Why should load_word == 8'b1 on startup?  Shouldn't load_word == 0 on
startup?

Nicholas
I imagine load_word is 0 on start-up. Where do you ever set
led to zero? I only see one line with "led <= 1'b1;"
With no other assignments, synthesis could very well
optimize this to a constant 1.

Regards,
Gabor
 
On Nov 25, 2:59 pm, Nicholas Kinar <n.ki...@usask.ca> wrote:
Hello Gabor--

Once again, thank you so much for your reply!



I imagine load_word is 0 on start-up.  Where do you ever set
led to zero?  I only see one line with "led <= 1'b1;"
With no other assignments, synthesis could very well
optimize this to a constant 1.

Regards,
Gabor

Yes, you are right about there being no other assignments to "led," and
it is probably the case that synthesis would optimize this.  So "led"
remains high.  Where could I update my code to ensure that led = 0 at
startup?

Perhaps I could update this block:

always @(posedge clk) begin
     if ( (load_word == 1) && (count < 16) ) count <= count + 1'b1;
     if (count == 16) count <= 0;
     if (load_word == 0) begin
         count <= 0;
         led <= 0;
     end
end

However, when I try to place "led <= 0" in this block, I obtain another
synthesis error complaining about "multiple drivers."

What is the "best practice" to ensure state of "led" at startup?  Is the
initial keyword synthesizable in some manner?

Many thanks--

Nicholas
You'd probably benefit from reading a good guide to synthesizable
Verilog, if someone has one to suggest...

Basically you can't use more than one block to drive the same
register for synthesis. Also note than since one block is
rising edge driven and another falling edge driven the
synthesis tool wouldn't be able to do what you're asking
unless it had dual-edge triggered flip-flops anyway.

The standard way to reset a signal at power up is to
add a reset term to the same block:

always @ (negedge clk)
if (reset)
begin
. . .
add your start-up assignments here
. . .
end
else
begin
. . .
Normal assignments while running
. . .
end

The block above implements a synchronous reset,
but you could make it asynchronous by adding
reset to the sensitivity list like:
always @ (negedge clk or posedge reset)
.. . .

If you don't actually have a reset signal to your design
you can usually tie the signal off somewhere, for
example:

assign reset = 0;

and the synthesis tools will still honor your reset
conditions at startup time. However it's usually best
to have a real reset on power-up.

Also if you're not really trying to make a latch for
led, and you just wanted to turn on the light when
load_word == 1, you can just write:
led <= (load_word == 1);
instead of
if (load_word == 1) led <= 1'b1;

The latter sets led but never clears it.

HTH,
Gabor
 
You'd probably benefit from reading a good guide to synthesizable
Verilog, if someone has one to suggest...
I've searched around for a good one, but most books on Verilog appear to
be geared toward simulation. There seems to be a great gap between
theory and practice in Verilog - one that could be easily filled by a
good book. Perhaps you or someone else on this newsgroup could consider
writing one. I'm certain that O'Reilly would publish it...


Basically you can't use more than one block to drive the same
register for synthesis. Also note than since one block is
rising edge driven and another falling edge driven the
synthesis tool wouldn't be able to do what you're asking
unless it had dual-edge triggered flip-flops anyway.

The standard way to reset a signal at power up is to
add a reset term to the same block:
I never really thought about this. It appears to be a good way to
ensure state. Most circuits do indeed have a /RESET signal, so it would
be beneficial to simply hook one pin of the FPGA up to this signal.


always @ (negedge clk)
if (reset)
begin
. . .
add your start-up assignments here
. . .
end
else
begin
. . .
Normal assignments while running
. . .
end

The block above implements a synchronous reset,
but you could make it asynchronous by adding
reset to the sensitivity list like:
always @ (negedge clk or posedge reset)
. . .

If you don't actually have a reset signal to your design
you can usually tie the signal off somewhere, for
example:

assign reset = 0;

Quartus II seems to complain about signals "stuck at GND or VCC," but
this works marvelously well. (Another warning added to the list of
multiple warnings.) I suppose that I would then not have to flip the
state of reset to turn the synthesized circuit back to "normal" operation.


and the synthesis tools will still honor your reset
conditions at startup time. However it's usually best
to have a real reset on power-up.
Agreed. I don't have a dedicated reset pin on my hardware board, but
this will be added on future PCBs that I'll create for my research projects.

Also if you're not really trying to make a latch for
led, and you just wanted to turn on the light when
load_word == 1, you can just write:
led <= (load_word == 1);
instead of
if (load_word == 1) led <= 1'b1;

The latter sets led but never clears it.
That's great to know!

Thank you once again for your help, Gabor! This really helps to get me
started with my design.

Nicholas
 
Hello--

Thank you once again to gabor and Andy who replied to this thread. Your
help is greatly appreciated!

Once again, I am working on the Verilog code to communicate with my DAC.
I have tried almost everything to get this to work, but the code does
not seem to be cooperating.

I have used the Quartus II SignalTap II Logic Analyzer tools to examine
what is happening with the signals. Apparently the only pin that
changes state is the "sdin_dac." This is the MOSI serial data input for
my DAC.

However, the SignalTap II Logic Analyzer shows that "sdin_dac" jams
high, and does not return to a low state after the control word has been
shifted out.

Essentially what I would like to accomplish is the following:

(1) When "load_word = 1," take "fsync_dac" high. Then take the 16-bit
contents of "data" register and shift it out via output pin "sdin_dac."
The DAC8580 datasheet from Texas Instruments states that the "Input
data is latched into the device input shift register on the rising edge
of SCLK, MSB first." The system "clk" is 30MHz, which is the same speed
as the maximum SPI bus speed of the DAC.

(2) Only shift out 16 bits, and then reset "load_word = 0."

(3) Take "fynsc_dac" low.


Using I2C, I can read the load_word register before and after I write to
it, and apparently on both reads "load_word = 0." This may demonstrate
that the register is being reset!

I think that what I am trying to accomplish can be done within less than
15 lines of code.

What is going wrong with my code, and could someone perhaps steer me in
the right direction?

Nicholas



Here is the complete code of my module which I hope to use to
communicate with the DAC:


// Module to update the DAC via SPI
module dac(
clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
sclk_dac,
fsync_dac,
reset_lw);


input clk; // system clock
input load_word; // if high, loads the word

input [7:0] msb_data; // msb of data
input [7:0] lsb_data; // lsb of data

output sdin_dac; // mosi for the dac
output sclk_dac; // sclk for the dac
output fsync_dac; // CS for the dac

output reset_lw; // output to reset the load_word

reg [15:0] data;
reg [4:0] count;

// Clock to DAC is continuous
assign sclk_dac = clk;


// Data is the concatenation of two registers
assign data = {msb_data, lsb_data};


// reset count
always @(posedge clk) begin
if ( (count < 16) && (load_word == 8'b1) )
count <= count + 1'b1;
if (count == 16) count <= 0;
if (load_word == 0) count <= 0;
end


always @(negedge clk) begin

if (load_word == 8'b1) begin

// CS is set high
if(count == 0) fsync_dac <= 1'b1;

// shift out the data
if(count < 16) sdin_dac <= data[15 - count];

if(count == 16) begin
fsync_dac <= 1'b0; // lower CS
sdin_dac <= 1'b0; // lower sdin
reset_lw <= 1'b1; // reset the load_word
end

end // if load_word block

// change reset_lw back again to zero
if (load_word == 8'b0) reset_lw <= 1'b0;
end // clk always

endmodule
 
Nicholas Kinar wrote:
Hello--

Thank you once again to gabor and Andy who replied to this thread. Your
help is greatly appreciated!


Once again, I am working on the Verilog code to communicate with my DAC.
I have tried almost everything to get this to work, but the code does
not seem to be cooperating.
Write a testbench and simulate the module dump all of the signals into a
vcd file. Use gtkwave to see what is happening to the internal signals.

Do behavioural simulations until it is close to what you want. After
thet try a synthesis run. My personal preference of course. With a
behvioural simulation you can sprinkle $display statements to see what
is happening. Single stepping the code would be better still

Is it doing what you expected?

See some of my comments inline.

I have used the Quartus II SignalTap II Logic Analyzer tools to examine
what is happening with the signals. Apparently the only pin that
changes state is the "sdin_dac." This is the MOSI serial data input for
my DAC.



What is going wrong with my code, and could someone perhaps steer me in
the right direction?
There is a bit of C describing the protocol in C on the wiki page
referred below.
Nicholas



Here is the complete code of my module which I hope to use to
communicate with the DAC:


// Module to update the DAC via SPI
module dac(
clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
sclk_dac,
fsync_dac,
reset_lw);


input clk; // system clock
input load_word; // if high, loads the word

input [7:0] msb_data; // msb of data
input [7:0] lsb_data; // lsb of data
I was always under the impression that the address used by SPI and the
data used by SPI were the same size and are 16 bits long.

Shouldn't there be a command word? The freescale documentation describes
various SPI registers such as in reference 1 on this web page:-
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
output sdin_dac; // mosi for the dac
output sclk_dac; // sclk for the dac
output fsync_dac; // CS for the dac

output reset_lw; // output to reset the load_word

reg [15:0] data;
reg [4:0] count;

// Clock to DAC is continuous
assign sclk_dac = clk;


// Data is the concatenation of two registers
assign data = {msb_data, lsb_data};


// reset count
always @(posedge clk) begin
if ( (count < 16) && (load_word == 8'b1) )
count <= count + 1'b1;
if (count == 16) count <= 0;
if (load_word == 0) count <= 0;
end


always @(negedge clk) begin

if (load_word == 8'b1) begin

// CS is set high
if(count == 0) fsync_dac <= 1'b1;

// shift out the data
if(count < 16) sdin_dac <= data[15 - count];
Since sdin_dac is one bit wide all you will ever see is data[15] or
possibly data[count]. A one bit range would be sensible here such as
sdin_dac<=data[count];

Please please try simulating this with a testbench. Andy
if(count == 16) begin
fsync_dac <= 1'b0; // lower CS
sdin_dac <= 1'b0; // lower sdin
reset_lw <= 1'b1; // reset the load_word
end

end // if load_word block

// change reset_lw back again to zero
if (load_word == 8'b0) reset_lw <= 1'b0;
end // clk always

endmodule
 
Hello Andy--

Thank you again for your response!


Write a testbench and simulate the module dump all of the signals into a
vcd file. Use gtkwave to see what is happening to the internal signals.

Do behavioural simulations until it is close to what you want. After
thet try a synthesis run. My personal preference of course. With a
behvioural simulation you can sprinkle $display statements to see what
is happening. Single stepping the code would be better still
I am new to working with Verilog, and simulation does indeed seem to be
the way to go with this language. The problem with me writing
testbenches is that I can never really be certain if what I want is what
is necessary to work with the actual electronics on the PCB! For many
years I have been working with C.

Is it doing what you expected?
I will write a testbench for my code and see what is really happening.

There is a bit of C describing the protocol in C on the wiki page
referred below.

I was always under the impression that the address used by SPI and the
data used by SPI were the same size and are 16 bits long.

Shouldn't there be a command word? The freescale documentation describes
various SPI registers such as in reference 1 on this web page:-
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
Hmm - I don't know if the DAC is that complex. In the past, I've
written code for a microcontroller to communicate with other chips over
SPI, and there has been a command word. I believe that this part
(DAC8580 from Texas Instruments) is simply a device that accepts a
control word. It doesn't appear to have registers.

Thank you for the Wikipedia link. Interesting resource, isn't it?

Please please try simulating this with a testbench. Andy

Will do, Andy - and I will post back here what I have learned. Thank
you so much for helping to point me in a proper direction.


Nicholas
 
Please please try simulating this with a testbench. Andy


Will do, Andy - and I will post back here what I have learned.
Hello--

I have now written a testbench for my code and verified that the
waveforms are exactly what I wanted. I ran my simulation using Icarus
Verilog. Gtkwave is a marvelous program, and I am very pleased with the
outcome of the simulation.

However, when Quartus II attempts to synthesize the Verilog code, I
receive the following errors:

Error (10028): Can't resolve multiple constant drivers for net
"sdin_dac" at dac.v(58)
Error (10029): Constant driver at dac.v(36)
Error (10028): Can't resolve multiple constant drivers for net
"fsync_dac" at dac.v(58)
Error (10028): Can't resolve multiple constant drivers for net
"reset_lw" at dac.v(58)
Error: Can't elaborate user hierarchy "dac:MyDac"Error: Quartus II
Analysis & Synthesis was unsuccessful. 5 errors, 0 warnings
Error: Quartus II Full Compilation was unsuccessful. 7 errors, 0 warnings


Here is the final version of my code which can be simulated with Icarus
Verilog. I have included code for "module testbench," which is the
testbench, and for "module dac," which is the code used to access the DAC.

What would I have to change to be able to synthesize this code? The one
error location is marked in the code below.

Note that I am not attempting to subject the testbench to synthesis. I
am only synthesizing the "module dac" in the code below as "HERE IS
WHERE THE SYNTHESIS ERROR OCCURS."

In the synthesizable version of the code, input rst is being tied off
via "assign rst = 0."

Once again, thank you for your help.

Nicholas

----------------------------------------

module testbench;

reg rst;
reg clk;
reg [7:0] load_word;
reg [7:0] msb_data;
reg [7:0] lsb_data;

wire sdin_dac;
wire fsync_dac;
wire reset_lw;

// Dump output of the model
initial
begin
$dumpfile("test.lxt");
$dumpvars(0,device_under_test);
end


//---create the DUT---
dac device_under_test( rst,
clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
fsync_dac,
reset_lw);

//---ensure initial state of variables---
initial begin
clk = 1'b0;
load_word = 8'b0;
rst = 1'b1;
end

//---generate clk---
always begin
#1 clk = 1'b0;
#1 clk = 1'b1;
end


// reset load_word
always @(posedge clk) begin
if(reset_lw == 1'b1) load_word <= 8'b0;
end


//---load the initial values---
initial begin
msb_data = 8'b01010101;
lsb_data = 8'b01010101;
end


//---bring load_word high---
initial begin
# 3 rst = 1'b0;
load_word = 8'b1;
# 20 begin
$display("Simulation complete");
end
end

endmodule

----------------------------------------

// Module to update the DAC via SPI
module dac( rst,
clk,
load_word,
msb_data,
lsb_data,
sdin_dac,
fsync_dac,
reset_lw);


input rst; // used to ensure state on startup
input clk;
input [7:0] load_word;

input [7:0] msb_data;
input [7:0] lsb_data;

output sdin_dac;
output fsync_dac;
output reset_lw;

reg sdin_dac;
reg fsync_dac;
reg reset_lw;

reg [15:0] data;
reg [4:0] count;


// ERROR: CONSTANT DRIVER IN THIS ALWAYS BLOCK
always @(posedge clk) begin
// reset logic
if (rst) begin
sdin_dac <= 1'b0;
fsync_dac <= 1'b0;
reset_lw <= 1'b0;
count <= 5'b0;
end
// normal logic
else begin
if ( (count < 16) && (load_word == 8'b1) )
count <= count + 1'b1;
if (count == 16) count <= 5'b0;
if (load_word == 1'b0) begin
sdin_dac <= 1'b0;
fsync_dac <= 1'b0;
reset_lw <= 1'b0;
count <= 5'b0;
end
end
end

// HERE IS WHERE THE SYNTHESIS ERROR OCCURS
// CANNOT RESOLVE MULTIPLE CONSTANT DRIVERS FOR:
// "sdin_dac", "fysnc_dac", and "reset_lw"

always @(negedge clk) begin
if (load_word == 8'b1) begin

if(count == 5'b0) begin
sdin_dac <= 1'b0;
fsync_dac <= 1'b1;
data <= {msb_data, lsb_data};
end

// shift out the data
if( (count < 16) && (count >= 1))
sdin_dac <= data[15 - count];

// logic should go back to zero again
if(count == 16) begin
sdin_dac <= 1'b0;
fsync_dac <= 1'b0;
reset_lw <= 1'b1;
end
end

// change reset_lw back again to zero
if (load_word == 1'b0) reset_lw <= 1'b0;
end

endmodule
 
On Nov 28, 10:10 am, Nicholas Kinar <n.ki...@usask.ca> wrote:
Please please try simulating this with a testbench. Andy

Will do, Andy - and I will post back here what I have learned.

Hello--

I have now written a testbench for my code and verified that the
waveforms are exactly what I wanted.  I ran my simulation using Icarus
Verilog.  Gtkwave is a marvelous program, and I am very pleased with the
outcome of the simulation.

However, when Quartus II attempts to synthesize the Verilog code, I
receive the following errors:

Error (10028): Can't resolve multiple constant drivers for net
"sdin_dac" at dac.v(58)
Error (10029): Constant driver at dac.v(36)
Error (10028): Can't resolve multiple constant drivers for net
"fsync_dac" at dac.v(58)
Error (10028): Can't resolve multiple constant drivers for net
"reset_lw" at dac.v(58)
Error: Can't elaborate user hierarchy "dac:MyDac"Error: Quartus II
Analysis & Synthesis was unsuccessful. 5 errors, 0 warnings
Error: Quartus II Full Compilation was unsuccessful. 7 errors, 0 warnings

Here is the final version of my code which can be simulated with Icarus
Verilog.  I have included code for "module testbench," which is the
testbench, and for "module dac," which is the code used to access the DAC..

What would I have to change to be able to synthesize this code?  The one
error location is marked in the code below.

Note that I am not attempting to subject the testbench to synthesis.  I
am only synthesizing the "module dac" in the code below as "HERE IS
WHERE THE SYNTHESIS ERROR OCCURS."

In the synthesizable version of the code, input rst is being tied off
via "assign rst = 0."

Once again, thank you for your help.

Nicholas

----------------------------------------

module testbench;

reg rst;
reg clk;
reg [7:0] load_word;
reg [7:0] msb_data;
reg [7:0] lsb_data;

wire sdin_dac;
wire fsync_dac;
wire reset_lw;

// Dump output of the model
initial
  begin
     $dumpfile("test.lxt");
     $dumpvars(0,device_under_test);
  end

//---create the DUT---
dac device_under_test( rst,
                       clk,
                       load_word,
                       msb_data,
                       lsb_data,
                       sdin_dac,
                       fsync_dac,
                       reset_lw);

//---ensure initial state of variables---
initial begin
    clk = 1'b0;
    load_word = 8'b0;
    rst = 1'b1;
end

  //---generate clk---
always begin
    #1  clk = 1'b0;
    #1  clk = 1'b1;
end

// reset load_word
always @(posedge clk) begin
        if(reset_lw == 1'b1) load_word <= 8'b0;
end

//---load the initial values---
initial begin
        msb_data = 8'b01010101;
        lsb_data = 8'b01010101;
end

//---bring load_word high---
initial begin
         # 3 rst = 1'b0;
             load_word = 8'b1;
         # 20 begin
                $display("Simulation complete");      
         end
end

endmodule

----------------------------------------

//  Module to update the DAC via SPI
module dac(     rst,
                clk,
                load_word,
                msb_data,
                lsb_data,
                sdin_dac,
                fsync_dac,
                reset_lw);

input rst;  // used to ensure state on startup
input clk;                                                                                      
input [7:0] load_word;          

input [7:0] msb_data;  
input [7:0] lsb_data;

output sdin_dac;                                        
output fsync_dac;              
output reset_lw;                        

reg sdin_dac;
reg fsync_dac;
reg reset_lw;

reg [15:0] data;
reg [4:0] count;                                        

// ERROR: CONSTANT DRIVER IN THIS ALWAYS BLOCK
always @(posedge clk) begin
        // reset logic
        if (rst) begin
                sdin_dac  <= 1'b0;
                fsync_dac <= 1'b0;
                reset_lw  <= 1'b0;
                count     <= 5'b0;
        end
        // normal logic
        else begin
                if ( (count < 16) && (load_word == 8'b1) )
                        count <= count + 1'b1;
                if (count == 16) count <= 5'b0;
                if (load_word == 1'b0) begin
                        sdin_dac  <= 1'b0;
                        fsync_dac <= 1'b0;
                        reset_lw  <= 1'b0;
                        count     <= 5'b0;
                end
        end
end

// HERE IS WHERE THE SYNTHESIS ERROR OCCURS
// CANNOT RESOLVE MULTIPLE CONSTANT DRIVERS FOR:
// "sdin_dac", "fysnc_dac", and "reset_lw"

always @(negedge clk) begin
                if (load_word == 8'b1) begin

                        if(count == 5'b0) begin
                           sdin_dac <= 1'b0;
                           fsync_dac <= 1'b1;
                           data <= {msb_data, lsb_data};
                        end

                        // shift out the data
                        if( (count < 16) && (count >= 1))
                                sdin_dac <= data[15 - count];

                        // logic should go back to zero again
                        if(count == 16) begin
                                sdin_dac <= 1'b0;
                                fsync_dac <= 1'b0;
                                reset_lw <= 1'b1;            
                        end
                end    

                // change reset_lw back again to zero
                if (load_word == 1'b0) reset_lw <= 1'b0;    
end

endmodule
You have these signals in two separate always blocks.
Synthesis will not work that way. Also your blocks
are using both edges of clk (one edge for each block
that is). So even trying to rewrite this as one block
will require some change to your logic. Do these
signals really need to change on both edges of clk?
What kind of flip-flop did you want Quartus to make
from this code?

HTH,
Gabor
 
You have these signals in two separate always blocks.
Synthesis will not work that way. Also your blocks
are using both edges of clk (one edge for each block
that is). So even trying to rewrite this as one block
will require some change to your logic. Do these
signals really need to change on both edges of clk?
What kind of flip-flop did you want Quartus to make
from this code?

HTH,
Gabor
Hello Gabor--

Once again, thank you so much for your response! I think that this is
beginning to make more sense to me...

Apparently now I need to re-write the logic so that the signals do not
change on both edges of clk. The signals *must* change within the same
block, which will always be triggered on the negedge of clk. I think
that it is possible to be able to do this.

I am going to play around at little bit with the logic in this block,
and I will make another posting back in this thread to show what I have
done.

Once again, many thanks for helping to point me in the right direction.


Nicholas
 

Welcome to EDABoard.com

Sponsor

Back
Top