Initial commit

This commit is contained in:
Tifer King 2025-04-05 02:57:08 +08:00
parent 2feb5c5ba3
commit 361c36a6e8
7 changed files with 438 additions and 2 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/.bloop
/.metals
/.vscode
/project
/target
/simWorkspace
/output

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Public
Copyright (c) 2025 15inTech(www.15zk.net)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,2 +1,5 @@
# Adaptive_Direct_Digital_Synthesis
# 自适应直接信号合成器
[使用手册](https://wiki.15zk.net/zh/opensource/Adaptive_Direct_Digital_Synthesis/manual)
[应用案例: 使用IW-RFSOC-2T2R生成宽带信号]()

16
build.sbt Normal file
View File

@ -0,0 +1,16 @@
ThisBuild / version := "1.0"
ThisBuild / scalaVersion := "2.13.14"
ThisBuild / organization := "cn.tiferking"
val spinalVersion = "1.12.0"
val spinalCore = "com.github.spinalhdl" %% "spinalhdl-core" % spinalVersion
val spinalLib = "com.github.spinalhdl" %% "spinalhdl-lib" % spinalVersion
val spinalIdslPlugin = compilerPlugin("com.github.spinalhdl" %% "spinalhdl-idsl-plugin" % spinalVersion)
lazy val root = (project in file("."))
.settings(
Compile / scalaSource := baseDirectory.value / "src",
libraryDependencies ++= Seq(spinalCore, spinalLib, spinalIdslPlugin)
)
fork := true

18
src/Config.scala Normal file
View File

@ -0,0 +1,18 @@
package project.config
import spinal.core._
import spinal.core.sim._
object Config {
def spinal = SpinalConfig(
targetDirectory = "output",
defaultConfigForClockDomains = ClockDomainConfig(
resetActiveLevel = LOW,
resetKind = SYNC,
clockEdge = RISING
),
onlyStdLogicVectorAtTopLevelIo = true
)
def sim = SimConfig.withConfig(spinal)
}

297
src/adds/Adds.scala Normal file
View File

@ -0,0 +1,297 @@
package adds
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba4.axis._
import spinal.lib.bus.amba4.axi.Axi4SpecRenamer
import spinal.lib.bus.amba4.axis.Axi4Stream._
import spinal.lib.bus.amba4.axilite._
import spinal.lib.io._
import project.config._
case class Adds(phaseCh: Int, portBit: Int, resolutionBit: Int, sampleBit: Int, dualport: Boolean = false, simulation: Boolean = false) extends Component {
val sampleCount = Math.pow(2, sampleBit).toInt;
val regBit = 32;
object Mode extends SpinalEnum(defaultEncoding = binarySequential) {
val TONE, LFM, SFM, FMCW = newElement()
}
val io = new Bundle {
val AddsOut = master(Axi4Stream(Axi4StreamConfig(phaseCh * (portBit / 8))))
val AddsOutQ = dualport generate master(Axi4Stream(Axi4StreamConfig(phaseCh * (portBit / 8))))
val Cfg = slave(AxiLite4(10,32))
//Ports for simulations
val Sim = simulation generate (out Bits(portBit bits))
val SimQ = simulation generate (out Bits(portBit bits))
}
noIoPrefix();
Axi4SpecRenamer(io.AddsOut);
AxiLite4SpecRenamer(io.Cfg);
val CfgClockDomain = ClockDomain.external("Cfg");
val CfgArea = new ClockingArea(CfgClockDomain) {
val resetSoftCfg = Reg(Bool()) init(False);
val enSoftCfg = Reg(Bool()) init(False);
val modeWorkCfg = Reg(Mode()) init(Mode.TONE);
val phaseCfg = Reg(UInt(32 bits)) init(0);
val dphaseCfg = Reg(UInt(32 bits)) init(0);
val ddphaseCfg = Reg(SInt(32 bits)) init(0);
val reloadCfg = Reg(UInt(32 bits)) init(0);
val thresholdCfg = Reg(UInt(32 bits)) init(0);
val stepholdCfg = Reg(UInt(32 bits)) init(0);
val resetSoftReadbackX = Reg(Bool()) init(False) addTag(crossClockDomain);
val resetSoftReadback = RegNext(resetSoftReadbackX);
when (resetSoftReadback) {
//Soft reset handshake
resetSoftCfg.clear();
}
when (resetSoftCfg) {
//Soft reset register, clear all
enSoftCfg.clearAll();
modeWorkCfg.clearAll();
phaseCfg.clearAll();
dphaseCfg.clearAll();
ddphaseCfg.clearAll();
reloadCfg.clearAll();
thresholdCfg.clearAll();
stepholdCfg.clearAll();
}
val axiArea = new Area{
// !simulation for fix the simulation master bug.
val axiSlave = new AxiLite4SlaveFactory(io.Cfg, !simulation);
axiSlave.readAndWrite(resetSoftCfg, 0x00, 0, "Soft reset");
axiSlave.readAndWrite(enSoftCfg, 0x00, 1, "Soft enable");
axiSlave.readAndWrite(modeWorkCfg, 0x00, 8, "Working mode");
axiSlave.readAndWrite(phaseCfg, 0x04, 0, "Init phase");
axiSlave.readAndWrite(dphaseCfg, 0x08, 0, "Init frequence");
axiSlave.readAndWrite(ddphaseCfg, 0x0C, 0, "Init delt frequence");
axiSlave.readAndWrite(reloadCfg, 0x10, 0, "Reload delay");
axiSlave.readAndWrite(thresholdCfg, 0x14, 0, "Threshold for reload");
axiSlave.readAndWrite(stepholdCfg, 0x18, 0, "Step hold for SFM");
axiSlave.printDataModel();
}
}
//Reg cross clock domain
val resetSoftX = RegNext(CfgArea.resetSoftCfg) init(False) addTag(crossClockDomain);
val enSoftX = RegNext(CfgArea.enSoftCfg) init(False) addTag(crossClockDomain);
val modeWorkX = RegNext(CfgArea.modeWorkCfg) init(Mode.TONE) addTag(crossClockDomain);
val phaseX = RegNext(CfgArea.phaseCfg) init(0) addTag(crossClockDomain);
val dphaseX = RegNext(CfgArea.dphaseCfg) init(0) addTag(crossClockDomain);
val ddphaseX = RegNext(CfgArea.ddphaseCfg) init(0) addTag(crossClockDomain);
val reloadX = RegNext(CfgArea.reloadCfg) init(0) addTag(crossClockDomain);
val thresholdX = RegNext(CfgArea.thresholdCfg) init(0) addTag(crossClockDomain);
val stepholdX = RegNext(CfgArea.stepholdCfg) init(0) addTag(crossClockDomain);
val resetSoft = RegNext(resetSoftX) init(False);
val enSoft = RegNext(enSoftX) init(False);
val modeWork = RegNext(modeWorkX) init(Mode.TONE);
val phase = RegNext(phaseX) init(0);
val dphase = RegNext(dphaseX) init(0);
val ddphase = RegNext(ddphaseX) init(0);
val reload = RegNext(reloadX) init(0);
val threshold = RegNext(thresholdX) init(0);
val stephold = RegNext(stepholdX) init(0);
//Soft reset handshake
CfgArea.resetSoftReadbackX := resetSoft;
def sinTable = for(sampleIndex <- 0 until sampleCount) yield {
val sinValue = Math.sin(2 * Math.PI * sampleIndex / sampleCount);
S((sinValue * ((1 << resolutionBit) / 2 - 1)).toInt, resolutionBit bits);
}
val sinROM = Mem(SInt(resolutionBit bits), initialContent = sinTable);
//Working register
val phaseWork = Vec.fill(phaseCh)(Reg(UInt(regBit bits)) init(0));
val dphaseWork = Vec.fill(phaseCh)(Reg(UInt(regBit bits)) init(0));
val ddphaseWork = Reg(SInt(regBit bits)) init(0);
//Initialization procedure counter
val cntInit = Counter(0 to phaseCh + 5);
//Working register reload counter
val cntReload = Counter(regBit bits);
//Working register step hold counter
val cntStep = Counter(regBit bits);
//Reload event triggered by initialization procedure or self reload
val triggerReload = Reg(Bool()) init(False);
//Output buffer
val buffOut = Reg(Bits(phaseCh * portBit bits)) init(0);
//Output buffer for dual port
val buffOutQ = simulation generate (Reg(Bits(phaseCh * portBit bits)) init(0));
//Disable the first uncertain output of the buffer
val buffFirst = Reg(Bool()) init(True);
when(!enSoft) {
//Reset dds status and working register
phaseWork.clearAll();
dphaseWork.clearAll();
ddphaseWork.clearAll();
buffOut.clearAll();
dualport generate buffOutQ.clearAll();
io.AddsOut.valid := False;
dualport generate (io.AddsOutQ.valid := False);
cntInit.clear();
cntReload.clear();
triggerReload.clear();
cntStep.clear();
buffFirst.set();
} elsewhen(triggerReload) {
when(cntReload > reload || modeWork === Mode.FMCW) {
cntReload.clear();
triggerReload.clear();
cntInit.clear();
} otherwise {
cntReload.increment();
}
io.AddsOut.valid := True;
dualport generate (io.AddsOutQ.valid := True);
buffOut := 0;
dualport generate (buffOutQ := 0);
buffFirst.set();
} elsewhen(cntInit < cntInit.end) {
when(modeWork === Mode.TONE) {
ddphaseWork := 0;
for(i <- 0 until phaseCh)
{
dphaseWork(i) := dphase;
}
} elsewhen(modeWork === Mode.LFM || modeWork === Mode.FMCW) {
ddphaseWork := ddphase;
dphaseWork(0) := dphase;
for(i <- 1 until phaseCh)
{
dphaseWork(i) := (dphaseWork(i - 1).asSInt + ddphaseWork).asUInt;
}
} elsewhen(modeWork === Mode.SFM) {
ddphaseWork := ddphase;
for(i <- 0 until phaseCh)
{
dphaseWork(i) := dphase;
}
} otherwise {
ddphaseWork := 0;
for(i <- 0 until phaseCh)
{
dphaseWork(i) := dphase;
}
}
phaseWork(0) := phase;
for(i <- 1 until phaseCh)
{
phaseWork(i) := (phaseWork(i - 1) + dphaseWork(i));
}
io.AddsOut.valid := True;
dualport generate (io.AddsOutQ.valid := True);
buffOut := 0;
dualport generate (buffOutQ := 0);
cntInit.increment();
cntReload.increment();
cntStep.clear();
buffFirst.set();
} otherwise {
when(modeWork === Mode.TONE) {
ddphaseWork := 0;
for(i <- 0 until phaseCh)
{
dphaseWork(i) := dphase;
}
} elsewhen(modeWork === Mode.LFM || modeWork === Mode.FMCW) {
ddphaseWork := ddphase;
for(i <- 0 until phaseCh)
{
dphaseWork(i) := (dphaseWork(i).asSInt + (ddphaseWork * phaseCh).trim((log2Up(phaseCh) + 2) bits)).asUInt;
}
when((!ddphase.sign && dphaseWork(phaseCh - 1) > threshold) ||
(ddphase.sign && dphaseWork(phaseCh - 1) < threshold)) {
triggerReload := True;
}
} elsewhen(modeWork === Mode.SFM) {
when(cntStep < stephold) {
cntStep.increment();
} elsewhen(cntStep === stephold) {
cntStep.increment();
for(i <- 0 until phaseCh)
{
dphaseWork(i) := (dphaseWork(i).asSInt + ddphaseWork + (ddphaseWork * S(i).resize(log2Up(phaseCh) + 1)).trim((log2Up(phaseCh) + 1) bits)).asUInt;
}
} otherwise {
for(i <- 0 until phaseCh)
{
dphaseWork(i) := (dphaseWork(i).asSInt + (ddphaseWork * S(phaseCh - i -1).resize(log2Up(phaseCh) + 1)).trim((log2Up(phaseCh) + 1) bits)).asUInt;
}
cntStep.clear();
}
ddphaseWork := ddphase;
when((!ddphase.sign && dphaseWork(phaseCh - 1) > threshold) ||
(ddphase.sign && dphaseWork(phaseCh - 1) < threshold)) {
triggerReload := True;
}
}
for(i <- 0 until phaseCh)
{
phaseWork(i) := phaseWork(i) + (dphaseWork(i) * phaseCh).trim((log2Up(phaseCh) + 1) bits);
}
cntReload.increment();
io.AddsOut.valid := True;
dualport generate (io.AddsOutQ.valid := True);
when(buffFirst) {
buffOut := 0;
dualport generate (buffOutQ := 0);
buffFirst.clear();
} otherwise {
for(i <- 0 until phaseCh)
{
buffOut(i * portBit, portBit bits) := sinROM.readSync(phaseWork(i).round(regBit - sampleBit)).asBits.resizeLeft(portBit);
dualport generate (buffOutQ(i * portBit, portBit bits) := sinROM.readSync(phaseWork(i).round(regBit - sampleBit) + sampleCount / 4).asBits.resizeLeft(portBit));
}
}
}
// Init phase reg
io.AddsOut.data := buffOut;
dualport generate (io.AddsOutQ.data := buffOutQ);
val SimClockDomain = simulation generate ClockDomain.external("Sim");
val clockSim = simulation generate Bool();
simulation generate {
clockSim := clockDomain.readClockWire;
val SimArea = new ClockingArea(SimClockDomain) {
val cntSim = Counter(0 to phaseCh);
val clockIn = Bool() addTag(crossClockDomain);
clockIn := clockSim;
when(clockIn.rise()) {
cntSim.clear();
} otherwise {
cntSim.increment();
}
io.Sim := io.AddsOut.data(portBit * cntSim.value, portBit bits);
dualport generate (io.SimQ := buffOutQ(portBit * cntSim.value, portBit bits));
}
}
}
object GenAdds extends App {
Config.spinal.generateVerilog(Adds(16, 16, 12, 12)).printPruned();
}

View File

@ -0,0 +1,95 @@
package adds.sim
import spinal.core._
import spinal.lib._
import spinal.lib.bus.amba4.axis._
import spinal.lib.bus.amba4.axi.Axi4SpecRenamer
import spinal.lib.bus.amba4.axis.Axi4Stream._
import spinal.lib.bus.amba4.axilite._
import spinal.lib.io._
import project.config._
import spinal.core.sim._
import spinal.lib.bus.amba4.axilite.sim._
import adds._
import scala.util.control._
object AddsSim extends App {
Config.sim.withFstWave.compile(new Adds(16, 16, 12, 12, true, true)).doSim{ dut =>
dut.clockDomain.reset()
dut.CfgClockDomain.reset()
dut.CfgClockDomain.forkStimulus(250)
dut.clockDomain.forkStimulus(160)
dut.SimClockDomain.forkStimulus(10)
dut.clockDomain.waitRisingEdge()
dut.CfgClockDomain.waitRisingEdge()
dut.SimClockDomain.waitRisingEdge()
val master = AxiLite4Master(dut.io.Cfg, dut.CfgClockDomain)
// TONE
master.reset();
master.write(0x00, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x04, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x08, List(0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x00.toByte));
master.write(0x0C, List(0x00.toByte, 0x10.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x10, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x01.toByte));
master.write(0x14, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x80.toByte));
master.write(0x18, List(0x10.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x00, List(0x02.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
dut.io.AddsOut.ready #= true
dut.clockDomain.waitRisingEdge();
dut.clockDomain.waitEdge(100000);
dut.clockDomain.waitRisingEdge();
// LFM
master.reset();
master.write(0x00, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x04, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x08, List(0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x00.toByte));
master.write(0x0C, List(0x00.toByte, 0x10.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x10, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x01.toByte));
master.write(0x14, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x80.toByte));
master.write(0x18, List(0x10.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x00, List(0x02.toByte, 0x01.toByte, 0x00.toByte, 0x00.toByte));
dut.io.AddsOut.ready #= true
dut.clockDomain.waitRisingEdge();
dut.clockDomain.waitEdge(100000);
dut.clockDomain.waitRisingEdge();
// SFM
master.reset();
master.write(0x00, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x04, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x08, List(0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x00.toByte));
master.write(0x0C, List(0x00.toByte, 0x10.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x10, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x01.toByte));
master.write(0x14, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x80.toByte));
master.write(0x18, List(0x10.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x00, List(0x02.toByte, 0x02.toByte, 0x00.toByte, 0x00.toByte));
dut.io.AddsOut.ready #= true
dut.clockDomain.waitRisingEdge();
dut.clockDomain.waitEdge(100000);
dut.clockDomain.waitRisingEdge();
// FMCW
master.reset();
master.write(0x00, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x04, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x08, List(0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0x00.toByte));
master.write(0x0C, List(0x00.toByte, 0x10.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x10, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x01.toByte));
master.write(0x14, List(0x00.toByte, 0x00.toByte, 0x00.toByte, 0x80.toByte));
master.write(0x18, List(0x10.toByte, 0x00.toByte, 0x00.toByte, 0x00.toByte));
master.write(0x00, List(0x02.toByte, 0x03.toByte, 0x00.toByte, 0x00.toByte));
dut.io.AddsOut.ready #= true
dut.clockDomain.waitRisingEdge();
dut.clockDomain.waitEdge(100000);
}
}