Skip to content

Commit

Permalink
Finished initial DAC support. Fixes #32. DRC could probably be better?
Browse files Browse the repository at this point in the history
azonenberg committed May 23, 2017
1 parent ab976c9 commit 39f583b
Showing 7 changed files with 156 additions and 51 deletions.
1 change: 0 additions & 1 deletion doc/gp4-hdl.tex
Original file line number Diff line number Diff line change
@@ -221,7 +221,6 @@ \subsection{\namestyle{gp4par} Device Limitations}
\item Counters: Delay, edge detector, PWM, wake-sleep mode, counter cascading.
All clock sources other than on-chip oscillators.
\item ADC
\item DAC: Inputs from DCMP.
\item Wake-Sleep
\item DCMP/PWM: PWM mode. Inputs from ADC.
\item SPI: ADC buffer mode, parallel output to fabric, clock synchronization.
107 changes: 102 additions & 5 deletions src/gp4par/make_graphs.cpp
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ PARGraphNode* MakeNode(
Greenpak4BitstreamEntity* entity,
PARGraph* dgraph);

void InferExtraNodes(
bool InferExtraNodes(
Greenpak4Netlist* netlist,
Greenpak4Device* device,
PARGraph*& ngraph,
@@ -94,7 +94,8 @@ bool BuildGraphs(
return false;

//Infer extra support nodes for things that use hidden functions of others
InferExtraNodes(netlist, device, ngraph, ilmap);
if(!InferExtraNodes(netlist, device, ngraph, ilmap))
return false;

return true;
}
@@ -178,7 +179,7 @@ void ReplicateVREF(
/**
@brief Add extra nodes to handle dependencies between nodes that share hard IP under the hood
*/
void InferExtraNodes(
bool InferExtraNodes(
Greenpak4Netlist* netlist,
Greenpak4Device* device,
PARGraph*& ngraph,
@@ -214,8 +215,102 @@ void InferExtraNodes(
continue;
Greenpak4NetlistCell* netsrc = driver.m_cell;

//We found the source of the net!
LogVerbose("Found a DAC not driven by a power rail\n");
LogDebug("DAC \"%s\" is driven by %s \"%s\"\n",
cell->m_name.c_str(),
netsrc->m_type.substr(3).c_str(),
netsrc->m_name.c_str());
LogIndenter li;

//See if there's already a DCMP driven by the same signal.
vector<Greenpak4NetlistCell*> dcmps;
for(auto jt : net->m_nodeports)
{
if(jt.IsNull())
continue;
if(jt.m_cell->m_type == "GP_DCMP")
dcmps.push_back(jt.m_cell);
}

//If not, create one
if(dcmps.empty())
{
LogDebug("No DCMP driven by this cell, creating a dummy\n");
madeChanges = true;

//Monotonically increasing counter used to ensure unique node IDs
static unsigned int dcmp_id = 1;

//Create the cell
Greenpak4NetlistCell* dcmp = new Greenpak4NetlistCell(module);
dcmp->m_type = "GP_DCMP";

//Tie its negative input to our input
char tmp[128];
for(int i=0; i<8; i++)
{
snprintf(tmp, sizeof(tmp), "INN[%d]", i);
dcmp->m_connections[tmp].push_back(net);
}

//Power down the comparator so it stays out of our way and doesn't waste power
dcmp->m_connections["PWRDN"].push_back(vdd);

//Give it a name
snprintf(tmp, sizeof(tmp), "$auto$make_graphs.cpp:%d:dcmp$%u",
__LINE__,
dcmp_id ++);
dcmp->m_name = tmp;

//Set a special attribute on the cell so that we don't give a "has no loads" warning
dcmp->m_attributes["__IGNORE__NOLOAD__"] = "1";

//LOC it to DCMP1 to ensure correct routing
dcmp->m_attributes["LOC"] = "DCMP_1";

//Add the cell to the module
module->AddCell(dcmp);

//Create the PAR node for it
PARGraphNode* nnode = new PARGraphNode(ilmap[dcmp->m_type], dcmp);
dcmp->m_parnode = nnode;
ngraph->AddNode(nnode);

//Copy the netlist edges to the PAR graph
//TODO: automate this somehow? Seems error-prone to do it twice
for(int i=0; i<8; i++)
{
snprintf(tmp, sizeof(tmp), "INN[%d]", i);
netsrc->m_parnode->AddEdge(driver.m_portname, nnode, tmp);
}
vddn->AddEdge("OUT", nnode, "PWRDN");

dcmps.push_back(dcmp);
continue;
}

//If exactly one, LOC it to DCMP1
else if(dcmps.size() == 1)
{
//TODO: is it possible for IN+ to not be routable? If so, need to make a new DCMP to handle this
auto dcmp = *dcmps.begin();
dcmp->m_attributes["LOC"] = "DCMP_1";
}

//TODO: If there's MORE than one DCMP driven by this signal, need to ensure one of them is placed at DCMP1
else
{
LogError("Can't handle multiple DCMPs and a DAC all sharing the same input yet");
return false;
}
}

//Re-index the graph if we changed it
if(madeChanges)
{
LogNotice("Re-indexing graph because we inferred additional nodes..\n");
netlist->Reindex();
ngraph->IndexNodesByLabel();
madeChanges = false;
}

//Look for IOBs driven by GP_VREF cells
@@ -362,6 +457,8 @@ void InferExtraNodes(
ngraph->IndexNodesByLabel();
//madeChanges = false;
}

return true;
}

/**
3 changes: 1 addition & 2 deletions src/gp4par/par_main.cpp
Original file line number Diff line number Diff line change
@@ -220,8 +220,7 @@ bool PostPARDRC(PARGraph* netlist, Greenpak4Device* device)
//so nothing to do here
}

//Check for DACs not sharing input with DCMP1 negative, but also not a constant
//TODO: check for DACs with wrong input bit ordering or not same source for all bits
//TODO: Check for DACs not sharing input with DCMP1 negative, but also not a constant

//TODO: check for DCMPs with wrong input bit ordering or not same source for all bits

23 changes: 10 additions & 13 deletions src/greenpak4/Greenpak4DAC.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***********************************************************************************************************************
* Copyright (C) 2016 Andrew Zonenberg and contributors *
* Copyright (C) 2017 Andrew Zonenberg and contributors *
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General *
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) *
@@ -152,19 +152,15 @@ bool Greenpak4DAC::Save(bool* bitstream)
return false;
}

//DIN must be either ALL power rails, or NO power rails
int dinPower = 0;
for(unsigned int i=0; i<8; i++)
{
if(m_din[i].IsPowerRail())
dinPower ++;
}
if( (dinPower != 0) && (dinPower != 8) )
//Verify that all 8 bits of each input came from the same entity
//TODO: verify bit ordering?
for(int i=1; i<8; i++)
{
LogError(
"DRC: DAC input data must be driven by either a constant, the SPI bus, or a counter.\n"
"Mixing constant and variable bits is not allowed.\n");
return false;
if(m_din[i].GetRealEntity() != m_din[0].GetRealEntity())
{
LogError("All bits of GP_DAC DIN must come from the same source node\n");
return false;
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -188,6 +184,7 @@ bool Greenpak4DAC::Save(bool* bitstream)
//Input selector
//WTF, the config is flipped from DAC0 to DAC1??? (see SLG46620V table 40)
//This also applies to the SLG46140 (see SLG46140 table 28).
bool dinPower = (m_din[0].IsPowerRail());
if(m_dacnum == 0)
bitstream[m_cbaseInsel] = !dinPower;
else
62 changes: 38 additions & 24 deletions src/greenpak4/Greenpak4DigitalComparator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/***********************************************************************************************************************
* Copyright (C) 2016 Andrew Zonenberg and contributors *
* Copyright (C) 2017 Andrew Zonenberg and contributors *
* *
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General *
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) *
@@ -208,26 +208,33 @@ bool Greenpak4DigitalComparator::Save(bool* bitstream)
bitstream[m_configBase + 4] = m_dcmpMode;

//Verify that all 8 bits of each input came from the same entity
//TODO: verify bit ordering?
for(int i=1; i<8; i++)
{
if(m_inp[i].GetRealEntity() != m_inp[i].GetRealEntity())
if(m_inp[i].GetRealEntity() != m_inp[0].GetRealEntity())
{
LogError("All bits of GP_DCMP INP must come from the same source node\n");
return false;
}

if(m_inn[i].GetRealEntity() != m_inn[i].GetRealEntity())
if(m_inn[i].GetRealEntity() != m_inn[0].GetRealEntity())
{
LogError("All bits of GP_DCMP INN must come from the same source node\n");
return false;
}
}

//If null inputs, but not unused, complain
//Enable bit
//Set this true if our power-down input is anything but Vdd
bool enabled = (m_powerDown != m_device->GetPower());

//If null inputs, but not unused, complain.
//It's OK to be partially connected if we're powered down.
//This can happen if, for example, DCMP1 is being inferred to drive a DAC
bool noClock = (m_clock.IsPowerRail() && !m_clock.GetPowerRailValue());
bool noInP = (m_inp[0].IsPowerRail() && !m_inp[0].GetPowerRailValue());
bool noInN = (m_inn[0].IsPowerRail() && !m_inn[0].GetPowerRailValue());
if(noClock || noInP || noInN)
if( (noClock || noInP || noInN) && enabled )
{
LogError("Missing clock or input to DCMP_%d (%s clock, %s inP, %s inN)\n",
m_cmpNum,
@@ -238,6 +245,31 @@ bool Greenpak4DigitalComparator::Save(bool* bitstream)
return false;
}

//If the negative input is used, hook it up
unsigned int cbase = m_configBase + 7;
if(!noInN)
{
//Invalid input
if(m_innsels.find(m_inn[0]) == m_innsels.end())
{
LogError("Invalid DCMP input (tried to feed %s to %s INN)\n",
m_inn[0].GetDescription().c_str(), GetDescription().c_str());
return false;
}

//Valid input, hook it up
else
{
unsigned int sel = m_innsels[m_inn[0]];
bitstream[cbase + 3] = (sel & 1) ? true : false;
bitstream[cbase + 4] = (sel & 2) ? true : false;
}
}

//If disabled, skip remaining config
if(!enabled)
return true;

//configBase + 5 is input clock source (clkbuf5 = 0, clkbuf2 = 1, anything else = illegal)
auto entity = m_clock.GetRealEntity();
auto ck = dynamic_cast<Greenpak4ClockBuffer*>(entity);
@@ -260,16 +292,14 @@ bool Greenpak4DigitalComparator::Save(bool* bitstream)
bitstream[m_configBase + 6] = m_clockInvert;

//insert powerdown sync bit here for DCMP0, others don't have it
unsigned int cbase = m_configBase + 7;
if(m_cmpNum == 0)
{
bitstream[m_configBase + 7] = m_pdSync;
cbase ++;
}

//Enable bit
//Set this true if our power-down input is anything but Vdd
bitstream[cbase + 0] = (m_powerDown != m_device->GetPower());
bitstream[cbase + 0] = enabled;

//Invalid input
if(m_inpsels.find(m_inp[0]) == m_inpsels.end())
@@ -287,21 +317,5 @@ bool Greenpak4DigitalComparator::Save(bool* bitstream)
bitstream[cbase + 2] = (sel & 2) ? true : false;
}

//Invalid input
if(m_innsels.find(m_inn[0]) == m_innsels.end())
{
LogError("Invalid DCMP input (tried to feed %s to %s INN)\n",
m_inn[0].GetDescription().c_str(), GetDescription().c_str());
return false;
}

//Valid input, hook it up
else
{
unsigned int sel = m_innsels[m_inn[0]];
bitstream[cbase + 3] = (sel & 1) ? true : false;
bitstream[cbase + 4] = (sel & 2) ? true : false;
}

return true;
}
1 change: 0 additions & 1 deletion tests/greenpak4/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -78,7 +78,6 @@ function(add_greenpak4_bitstream name part)
COMMAND gp4par "--stdout-only"
--usercode 41
--quiet
--debug
--part ${part}
${pcfargs1}
${pcfargs2}
10 changes: 5 additions & 5 deletions tests/greenpak4/slg46620v/Dac.v
Original file line number Diff line number Diff line change
@@ -61,14 +61,14 @@ module Dac(bg_ok, vout, vout2, wave_sync);
// Oscillators

//The 1730 Hz oscillator
wire clk_108hz;
wire clk_1730hz;
GP_LFOSC #(
.PWRDN_EN(0),
.AUTO_PWRDN(0),
.OUT_DIV(16)
.OUT_DIV(1)
) lfosc (
.PWRDN(1'b0),
.CLKOUT(clk_108hz)
.CLKOUT(clk_1730hz)
);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -103,7 +103,7 @@ module Dac(bg_ok, vout, vout2, wave_sync);
(* LOC = "COUNT8_6" *)
(* COUNT_EXTRACT = "FORCE" *)
reg[7:0] count = COUNT_MAX;
always @(posedge clk_108hz) begin
always @(posedge clk_1730hz) begin
if(count == 0)
count <= COUNT_MAX;
else
@@ -121,7 +121,7 @@ module Dac(bg_ok, vout, vout2, wave_sync);
.COUNT_TO(COUNT_MAX),
.RESET_MODE("RISING")
) cnt (
.CLK(clk_108hz),
.CLK(clk_1730hz),
.RST(1'b0),
.OUT(wave_sync),
.POUT(count_pout)

0 comments on commit 39f583b

Please sign in to comment.