Skip to content

Commit

Permalink
Adding sine generator mode for dsmod
Browse files Browse the repository at this point in the history
  • Loading branch information
hpretl committed Oct 27, 2024
1 parent a21ac07 commit c2dd34a
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 10 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ This project implements a simple SPI where the loaded 16b data can be output in

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

Further, a sine generator with programmable frequency can be used to drive the DS-modulator.

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

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.

Further, a sine generator (based on a LUT) with programmable frequency can be selected to drive the input of the DAC.

## How to test

- 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.
- Check the dc output voltage of the delta-sigma modulator by using an external RC lowpass filter.
- Check the sine output 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. A voltmeter can be used to inspect the DAC output voltage.
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. If the sine generator is used for the DAC input, a scope can be used to monitor the sine signal.
6 changes: 3 additions & 3 deletions info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ pinout:
ui[1]: "SPI data in (MOSI)"
ui[2]: "SPI load (CS)"
ui[3]: "select output byte (0 = low, 1 = high)"
ui[4]: ""
ui[5]: ""
ui[6]: ""
ui[4]: "sinegen scale factor (LSB)"
ui[5]: "sinegen scale factor (MSB)"
ui[6]: "select ds-modulator input (0 = SPI register, 1 = sine generator)"
ui[7]: "order of delta-sigma modulator (0 = 1st, 1 = 2nd)"

# Outputs
Expand Down
74 changes: 74 additions & 0 deletions src/sinegen1.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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
*
* Simple sine-generator based on LUT.
*/

`default_nettype none
`ifndef __SINEGEN1__
`define __SINEGEN1__

module sinegen1 (
output wire [15:0] o_data,
input i_rst_n,
input i_clk,
input [15:0] i_step,
input [1:0] i_scale
);

reg [4:0] read_ptr_r;
reg [15:0] ctr_r;
reg ctr_msb_last_r;
wire [3:0] scale_w;

// this bitpattern is a 16b UINT sine with a period of 32 samples with
// 90% amplitude and an offset of 0x8000
/* verilator lint_off LITENDIAN */
localparam [0:(32*16)-1] sin_const = {
16'h8000,16'h9679,16'hAC16,16'hC000,16'hD175,16'hDFC9,16'hEA6E,16'hF0FD,
16'hF333,16'hF0FD,16'hEA6E,16'hDFC9,16'hD175,16'hC000,16'hAC16,16'h9679,
16'h8000,16'h6987,16'h53EA,16'h4000,16'h2E8B,16'h2037,16'h1592,16'h0F03,
16'h0CCD,16'h0F03,16'h1592,16'h2037,16'h2E8B,16'h4000,16'h53EA,16'h6987
};
/* verilator lint_on LITENDIAN */

assign scale_w = i_scale << 2;

assign o_data = sin_const[read_ptr_r*16 +: 16] >> scale_w;

always @(posedge i_clk or negedge i_rst_n) begin
if (i_rst_n === 1'b0) begin
// reset all registers
read_ptr_r <= 5'b0;
ctr_r <= 16'b0;
ctr_msb_last_r <= 1'b0;
end else begin
// the ctr is incremented by the step size control from outside
ctr_r <= ctr_r + i_step;
ctr_msb_last_r <= ctr_r[15];

// on a ctr overflow the read pointer is incremented; the input step
// allows thus to control the frequency of the generated sine
if ((ctr_r[15] === 1'b0) && (ctr_msb_last_r === 1'b1))
read_ptr_r <= read_ptr_r + 1'b1;
end
end

endmodule // sinegen1

`endif
`default_nettype wire
23 changes: 18 additions & 5 deletions src/tt_um_hpretl_spi.v
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
`default_nettype none
`include "chain2.v"
`include "dsmod1.v"
`include "sinegen1.v"

module tt_um_hpretl_spi (
input wire [7:0] ui_in, // Dedicated inputs
Expand All @@ -18,10 +19,14 @@ module tt_um_hpretl_spi (
input wire rst_n // reset_n - low to reset
);

wire [15:0] out_w;
wire [15:0] reg_out_w;
wire [15:0] sine_out_w;
wire [15:0] dac_input;

assign uio_oe = 8'b11111111; // using IO for output
assign uio_out = ui_in[3] ? out_w[15:8] : out_w[7:0];
assign uio_out = ui_in[3] ? reg_out_w[15:8] : reg_out_w[7:0];

assign dac_input = ui_in[6] ? sine_out_w : reg_out_w;

chain2 spi(
.i_resetn(rst_n),
Expand All @@ -32,22 +37,30 @@ module tt_um_hpretl_spi (
.o_spi_dat(uo_out[0]),
.o_det(uo_out[1]),
.o_check(uo_out[2]),
.o_data(out_w)
.o_data(reg_out_w)
);

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

sinegen1 sine(
.i_rst_n(rst_n),
.i_clk(clk),
.i_step(reg_out_w),
.i_scale(ui_in[5:4]),
.o_data(sine_out_w)
);

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

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

endmodule // tt_um_hpretl_spi
36 changes: 36 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ async def test_project(dut):
await ClockCycles(dut.clk, 1)
assert dut.uio_out.value == 0xca

dut._log.info("Check DSMOD minimum value")
# Shift 0 in
for i in range(16):
# b0 is clk, b1 is dat, b2 is load, b3 is select
Expand All @@ -92,6 +93,7 @@ async def test_project(dut):
# Wait a few cycles to watch DAC operation
await ClockCycles(dut.clk, 100)

dut._log.info("Check DSMOD maximum value")
# Shift 1 in
for i in range(16):
# b0 is clk, b1 is dat, b2 is load, b3 is select
Expand All @@ -115,3 +117,37 @@ async def test_project(dut):

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

dut._log.info("Check DSMOD driven by sinegen")

load_word = "3000"
load_word_bin = format(int(load_word, 16), '016b')
load_word_bits = [int(bit) for bit in load_word_bin]

# Shift frequency control 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*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
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
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)

# Now select sinegen and let run for a few clock cycles
# b6 is sinegen select
dut.ui_in.value = 2**6

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

0 comments on commit c2dd34a

Please sign in to comment.