Skip to content

Commit

Permalink
Adding DS-modulator
Browse files Browse the repository at this point in the history
  • Loading branch information
hpretl committed Oct 27, 2024
1 parent 9ffbf34 commit a21ac07
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

This project implements a simple SPI where the loaded 16b data can be output in 8b chunks (high and low byte). In addition, a magic cookie detection is implemented (an output goes active on detection of 0xCAFE).

In addition, a first- and second-order delta-sigma modulator is implemented create a simple low-frequency voltage output with 16b.

The input and output list documentation can be found in `info.yaml`.
9 changes: 6 additions & 3 deletions docs/info.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ You can also include images in this folder and reference them in the markdown. E

## How it works

A simple serial 16b register (mini SPI) with magic cookie detection is implemented.
A simple serial 16b register (mini SPI) with magic cookie detection is implemented. In addition, a 16bit sigma-delta modulator of 1st or 2nd order is included.

## How to test

Load the shift register in a serial way.
- Load the shift register in a serial way.
- Check the magic cookie detection.
- Check the digital output (low- and high-byte) of the loaded data word.
- Check the output voltage of the delta-sigma modulator by using an external RC lowpass filter.

## External hardware

Just a way to set digital inputs is needed. A scope for monitoring output signals would be good.
Just a way to set digital inputs is needed. A scope for monitoring output signals would be good. A voltmeter can be used to inspect the DAC output voltage.
7 changes: 4 additions & 3 deletions info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ project:
source_files:
- "tt_um_hpretl_spi.v"
- "chain2.v"
- "dsmod1.v"

# The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins.
pinout:
Expand All @@ -30,7 +31,7 @@ pinout:
ui[4]: ""
ui[5]: ""
ui[6]: ""
ui[7]: ""
ui[7]: "order of delta-sigma modulator (0 = 1st, 1 = 2nd)"

# Outputs
uo[0]: "SPI data out (MISO)"
Expand All @@ -39,8 +40,8 @@ pinout:
uo[3]: ""
uo[4]: ""
uo[5]: ""
uo[6]: ""
uo[7]: ""
uo[6]: "inverted output of delta-sigma modulator"
uo[7]: "output of delta-sigma modulator"

# Bidirectional pins
uio[0]: "register b0|8"
Expand Down
91 changes: 91 additions & 0 deletions src/dsmod1.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Harald Pretl
* Johannes Kepler University, Institute for Integrated Circuits
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*
* 16b Delta-Sigma Modulator with Single-Bit Output
*/

`default_nettype none
`ifndef __DSMOD1__
`define __DSMOD1__

module dsmod1(
input [15:0] i_data, // data in
input i_rst_n,
input i_clk,
input i_mode, // 0 = 1st order, 1 = 2nd order SD-modulator
output reg o_ds, // single-bit SD-modulator output
output wire o_ds_n // plus the complementary output
);

reg [15:0] accu1;
reg [15:0] accu2;
reg [1:0] accu3;

reg [1:0] mod2_ctr; // clk divide by 4 for 1st stage of 2nd order mod.
reg [1:0] mod2_out; // output 1st stage

localparam ORD1 = 1'd0;
localparam ORD2 = 1'd1;

// provide out and out_n to make levelshifter easier
assign o_ds_n = ~o_ds;

always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n == 1'b0) begin
// reset all registers
accu1 <= 16'b0;
accu2 <= 16'b0;
accu3 <= 2'b0;
o_ds <= 1'b0;
mod2_ctr <= 2'b0;
mod2_out <= 2'b0;
end else begin
// sd-modulator is running

if (i_mode === ORD1) begin
// delta-sigma modulator 1st order
// this simple structure works if everything
// is UINT
{o_ds,accu1} <= i_data + accu1;
end else if (i_mode === ORD2) begin
// delta-sigma modulator 2nd order
// the first stage runs on clk/4, the second
// stage runs on clk

if (mod2_ctr === 2'b0) begin
// this only happens every 4th clk
// cycle
{mod2_out,accu1} <= {2'b00,i_data}
+ {1'b0,accu1,1'b0}
+ 18'h10000
- {2'b0,accu2};
accu2 <= accu1;
end

// this is the clk divider for the 1st stage
mod2_ctr <= mod2_ctr + 1'b1;

// this simple structure is the 2nd stage
// running on clk
{o_ds,accu3} <= mod2_out + accu3;
end
end
end
endmodule // dsmod1

`endif
`default_nettype wire
16 changes: 13 additions & 3 deletions src/tt_um_hpretl_spi.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

`default_nettype none
`include "chain2.v"
`include "dsmod1.v"

module tt_um_hpretl_spi (
input wire [7:0] ui_in, // Dedicated inputs
Expand All @@ -22,7 +23,7 @@ module tt_um_hpretl_spi (
assign uio_oe = 8'b11111111; // using IO for output
assign uio_out = ui_in[3] ? out_w[15:8] : out_w[7:0];

chain2 dut(
chain2 spi(
.i_resetn(rst_n),
.i_clk(clk),
.i_spi_clk(ui_in[0]),
Expand All @@ -34,10 +35,19 @@ module tt_um_hpretl_spi (
.o_data(out_w)
);

dsmod1 dac(
.i_data(out_w),
.i_rst_n(rst_n),
.i_clk(clk),
.i_mode(ui_in[7]),
.o_ds(uo_out[7]),
.o_ds_n(uo_out[6])
);

// All output pins must be assigned. If not used, assign to 0.
assign uo_out[7:3] = 5'b10000;
assign uo_out[5:3] = 3'b000;

// List all unused inputs to prevent warnings
wire _unused = &{ena, uio_in[7:0], ui_in[7:4], 1'b0};
wire _unused = &{ena, uio_in[7:0], ui_in[6:4], 1'b0};

endmodule // tt_um_hpretl_spi
44 changes: 39 additions & 5 deletions test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ async def test_project(dut):

# Shift cookie in
for i in range(16):
#b0 is clk, b1 is dat, b2 is load, b3 is select
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*load_word_bits[i] + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

#b0 is clk, b1 is dat, b2 is load, b3 is select
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*1 + 2*load_word_bits[i] + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

assert (dut.uo_out.value & 2) == 0

# serial register is loaded, now store it
#b0 is clk, b1 is dat, b2 is load, b3 is select
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
await ClockCycles(dut.clk, 3)
dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0
Expand All @@ -70,14 +70,48 @@ async def test_project(dut):

# Shift 0 in
for i in range(16):
#b0 is clk, b1 is dat, b2 is load, b3 is select
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

#b0 is clk, b1 is dat, b2 is load, b3 is select
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*1 + 2*0 + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

assert (dut.uo_out.value & 2) == 2

# serial register is loaded, now store it
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
await ClockCycles(dut.clk, 3)
dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0
await ClockCycles(dut.clk, 3)

# Wait a few cycles to watch DAC operation
await ClockCycles(dut.clk, 100)

# Shift 1 in
for i in range(16):
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*1 + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

#b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*1 + 2*1 + 4*0 + 8*0

await ClockCycles(dut.clk, 3)

assert (dut.uo_out.value & 2) == 0

# serial register is loaded, now store it
# b0 is clk, b1 is dat, b2 is load, b3 is select
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
await ClockCycles(dut.clk, 3)
dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0
await ClockCycles(dut.clk, 3)

# Wait a few cycles to watch DAC operation
await ClockCycles(dut.clk, 100)

0 comments on commit a21ac07

Please sign in to comment.