/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.std.memory;
import java.util.ArrayList;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import com.bfh.logisim.designrulecheck.Netlist;
import com.bfh.logisim.designrulecheck.NetlistComponent;
import com.bfh.logisim.fpgagui.FPGAReport;
import com.bfh.logisim.hdlgenerator.AbstractHDLGeneratorFactory;
import com.bfh.logisim.settings.Settings;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.std.wiring.ClockHDLGeneratorFactory;
public class RamHDLGeneratorFactory extends AbstractHDLGeneratorFactory {
private static final String ByteArrayStr = "BYTE_ARRAY";
private static final int ByteArrayId = -1;
private static final String RestArrayStr = "REST_ARRAY";
private static final int RestArrayId = -2;
private static final String MemArrayStr = "MEMORY_ARRAY";
private static final int MemArrayId = -3;
@Override
public String getComponentStringIdentifier() {
return "RAM";
}
@Override
public SortedMap<String, Integer> GetInputList(Netlist TheNetlist,
AttributeSet attrs) {
SortedMap<String, Integer> Inputs = new TreeMap<String, Integer>();
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
Inputs.put("Address", attrs.getValue(Mem.ADDR_ATTR).getWidth());
Inputs.put("DataIn", NrOfBits);
Inputs.put("WE", 1);
Inputs.put("OE", 1);
Object trigger = attrs.getValue(StdAttr.TRIGGER);
boolean asynch = trigger.equals(StdAttr.TRIG_HIGH)
|| trigger.equals(StdAttr.TRIG_LOW);
if (!asynch) {
Inputs.put("Clock", 1);
Inputs.put("Tick", 1);
}
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
if (byteEnables) {
int NrOfByteEnables = Ram.GetNrOfByteEnables(attrs);
for (int i = 0; i < NrOfByteEnables; i++) {
Inputs.put("ByteEnable" + Integer.toString(i), 1);
}
}
return Inputs;
}
@Override
public SortedMap<String, Integer> GetMemList(AttributeSet attrs,
String HDLType) {
SortedMap<String, Integer> Mems = new TreeMap<String, Integer>();
if (HDLType.equals(Settings.VHDL)) {
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
if (byteEnables) {
boolean truncated = (NrOfBits % 8) != 0;
int NrOfByteEnables = Ram.GetNrOfByteEnables(attrs);
if (truncated) {
NrOfByteEnables--;
Mems.put("s_trunc_mem_contents", RestArrayId);
}
for (int i = 0; i < NrOfByteEnables; i++) {
Mems.put("s_byte_mem_" + Integer.toString(i) + "_contents",
ByteArrayId);
}
} else {
Mems.put("s_mem_contents", MemArrayId);
}
}
return Mems;
}
@Override
public ArrayList<String> GetModuleFunctionality(Netlist TheNetlist,
AttributeSet attrs, FPGAReport Reporter, String HDLType) {
ArrayList<String> Contents = new ArrayList<String>();
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
if (HDLType.equals(Settings.VHDL)) {
Contents.addAll(MakeRemarkBlock(
"Here the control signals are defined", 3, HDLType));
Contents.add(" s_RAM_enable <= s_TickDelayLine(0);");
if (byteEnables) {
for (int i = 0; i < Ram.GetNrOfByteEnables(attrs); i++) {
Contents.add(" s_byte_enable_" + Integer.toString(i)
+ " <= s_ByteEnableReg(" + Integer.toString(i)
+ ") AND s_TickDelayLine(1) AND s_OEReg;");
Contents.add(" s_we_" + Integer.toString(i)
+ " <= s_ByteEnableReg("
+ Integer.toString(i)
+ ") AND s_TickDelayLine(0) AND s_WEReg;");
}
} else {
Contents.add(" s_oe <= s_TickDelayLine(1) AND s_OEReg;");
Contents.add(" s_we <= s_TickDelayLine(0) AND s_WEReg;");
}
Contents.add("");
Contents.addAll(MakeRemarkBlock(
"Here the input registers are defined", 3, HDLType));
Contents.add(" InputRegs : PROCESS (Clock , Tick , Address , DataIn , WE , OE )");
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" IF (Tick = '1') THEN");
Contents.add(" s_DataInReg <= DataIn;");
Contents.add(" s_Address_reg <= Address;");
Contents.add(" s_WEReg <= WE;");
Contents.add(" s_OEReg <= OE;");
if (byteEnables) {
for (int i = 0; i < Ram.GetNrOfByteEnables(attrs); i++) {
Contents.add(" s_ByteEnableReg("
+ Integer.toString(i) + ") <= ByteEnable"
+ Integer.toString(i) + ";");
}
}
Contents.add(" END IF;");
Contents.add(" END IF;");
Contents.add(" END PROCESS InputRegs;");
Contents.add("");
Contents.add(" TickPipeReg : PROCESS(Clock , Tick , s_TickDelayLine)");// TODO
// :YSY
// suppress
// s_TickDelayLine
// from
// sensibility
// list
// (written
// in
// process)
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" s_TickDelayLine(0) <= Tick;");
Contents.add(" s_TickDelayLine(1) <= s_TickDelayLine(0);");
Contents.add(" END IF;");
Contents.add(" END PROCESS TickPipeReg;");
Contents.add("");
Contents.addAll(MakeRemarkBlock(
"Here the actual memorie(s) is(are) defined", 3, HDLType));
if (byteEnables) {
boolean truncated = (attrs.getValue(Mem.DATA_ATTR).getWidth() % 8) != 0;
for (int i = 0; i < Ram.GetNrOfByteEnables(attrs); i++) {
Contents.add(" Mem" + Integer.toString(i)
+ " : PROCESS( Clock , s_we_" + Integer.toString(i)
+ ", s_DataInReg, s_Address_reg, s_RAM_enable)");
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" IF (s_RAM_enable = '1') THEN");
Contents.add(" IF (s_we_" + Integer.toString(i)
+ " = '1') THEN");
int startIndex = i * 8;
int endIndex = (i == (Ram.GetNrOfByteEnables(attrs) - 1)) ? attrs
.getValue(Mem.DATA_ATTR).getWidth() - 1
: (i + 1) * 8 - 1;
String Memname = (i == (Ram.GetNrOfByteEnables(attrs) - 1) && truncated) ? "s_trunc_mem_contents"
: "s_byte_mem_" + Integer.toString(i) + "_contents";
Contents.add(" "
+ Memname
+ "(to_integer(unsigned(s_Address_reg))) <= s_DataInReg("
+ endIndex + " DOWNTO " + startIndex + ");");
Contents.add(" END IF;");
Contents.add(" s_ram_data_out(" + endIndex
+ " DOWNTO " + startIndex + ") <= " + Memname
+ "(to_integer(unsigned(s_Address_reg)));");
Contents.add(" END IF;");
Contents.add(" END IF;");
Contents.add(" END PROCESS Mem" + Integer.toString(i)
+ ";");
Contents.add("");
}
} else {
Contents.add(" Mem : PROCESS( Clock , s_we, s_DataInReg, s_Address_reg, s_RAM_enable)");
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" IF (s_RAM_enable = '1') THEN");
Contents.add(" IF (s_we = '1') THEN");
Contents.add(" s_mem_contents(to_integer(unsigned(s_Address_reg))) <= s_DataInReg;");
Contents.add(" END IF;");
Contents.add(" s_ram_data_out <= s_mem_contents(to_integer(unsigned(s_Address_reg)));");
Contents.add(" END IF;");
Contents.add(" END IF;");
Contents.add(" END PROCESS Mem;");
Contents.add("");
}
Contents.addAll(MakeRemarkBlock(
"Here the output register is defined", 3, HDLType));
if (byteEnables) {
for (int i = 0; i < Ram.GetNrOfByteEnables(attrs); i++) {
Contents.add(" Res" + Integer.toString(i)
+ " : PROCESS( Clock , s_byte_enable_"
+ Integer.toString(i) + ", s_ram_data_out)");
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" IF (s_byte_enable_"
+ Integer.toString(i) + " = '1') THEN");
int startIndex = i * 8;
int endIndex = (i == (Ram.GetNrOfByteEnables(attrs) - 1)) ? attrs
.getValue(Mem.DATA_ATTR).getWidth() - 1
: (i + 1) * 8 - 1;
Contents.add(" DataOut(" + endIndex + " DOWNTO "
+ startIndex + ") <= s_ram_data_out(" + endIndex
+ " DOWNTO " + startIndex + ");");
Contents.add(" END IF;");
Contents.add(" END IF;");
Contents.add(" END PROCESS Res" + Integer.toString(i)
+ ";");
Contents.add("");
}
} else {
Contents.add(" Res : PROCESS( Clock , s_oe, s_ram_data_out)");
Contents.add(" BEGIN");
Contents.add(" IF (Clock'event AND (Clock = '1')) THEN");
Contents.add(" IF (s_oe = '1') THEN");
Contents.add(" DataOut <= s_ram_data_out;");
Contents.add(" END IF;");
Contents.add(" END IF;");
Contents.add(" END PROCESS Res;");
Contents.add("");
}
}
return Contents;
}
@Override
public int GetNrOfTypes(Netlist TheNetlist, AttributeSet attrs,
String HDLType) {
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
return (byteEnables) ? ((NrOfBits % 8) == 0) ? 1 : 2 : 1;
}
@Override
public SortedMap<String, Integer> GetOutputList(Netlist TheNetlist,
AttributeSet attrs) {
SortedMap<String, Integer> Outputs = new TreeMap<String, Integer>();
Outputs.put("DataOut", attrs.getValue(Mem.DATA_ATTR).getWidth());
return Outputs;
}
@Override
public SortedMap<String, String> GetPortMap(Netlist Nets,
NetlistComponent ComponentInfo, FPGAReport Reporter, String HDLType) {
AttributeSet attrs = ComponentInfo.GetComponent().getAttributeSet();
SortedMap<String, String> PortMap = new TreeMap<String, String>();
Object trigger = attrs.getValue(StdAttr.TRIGGER);
boolean asynch = trigger.equals(StdAttr.TRIG_HIGH)
|| trigger.equals(StdAttr.TRIG_LOW);
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
PortMap.putAll(GetNetMap("Address", true, ComponentInfo, Mem.ADDR,
Reporter, HDLType, Nets));
int DinPin = (asynch) ? Ram.ADIN : Ram.SDIN;
PortMap.putAll(GetNetMap("DataIn", true, ComponentInfo, DinPin,
Reporter, HDLType, Nets));
PortMap.putAll(GetNetMap("WE", true, ComponentInfo, Ram.WE, Reporter,
HDLType, Nets));
PortMap.putAll(GetNetMap("OE", true, ComponentInfo, Ram.OE, Reporter,
HDLType, Nets));
if (!asynch) {
String SetBit = (HDLType.equals(Settings.VHDL)) ? "'1'" : "1'b1";
String ZeroBit = (HDLType.equals(Settings.VHDL)) ? "'0'" : "1'b0";
String BracketOpen = (HDLType.equals(Settings.VHDL)) ? "(" : "[";
String BracketClose = (HDLType.equals(Settings.VHDL)) ? ")" : "]";
if (!ComponentInfo.EndIsConnected(Ram.CLK)) {
Reporter.AddError("Component \"RAM\" in circuit \""
+ Nets.getCircuitName() + "\" has no clock connection!");
PortMap.put("Clock", ZeroBit);
PortMap.put("Tick", ZeroBit);
} else {
String ClockNetName = GetClockNetName(ComponentInfo, Ram.CLK,
Nets);
if (ClockNetName.isEmpty()) {
PortMap.putAll(GetNetMap("Clock", true, ComponentInfo,
Ram.CLK, Reporter, HDLType, Nets));
PortMap.put("Tick", SetBit);
} else {
int ClockBusIndex;
if (Nets.RequiresGlobalClockConnection()) {
ClockBusIndex = ClockHDLGeneratorFactory.GlobalClockIndex;
} else {
ClockBusIndex = (attrs.getValue(StdAttr.TRIGGER) == StdAttr.TRIG_RISING) ? ClockHDLGeneratorFactory.PositiveEdgeTickIndex
: ClockHDLGeneratorFactory.NegativeEdgeTickIndex;
}
PortMap.put(
"Clock",
ClockNetName
+ BracketOpen
+ Integer
.toString(ClockHDLGeneratorFactory.GlobalClockIndex)
+ BracketClose);
PortMap.put(
"Tick",
ClockNetName + BracketOpen
+ Integer.toString(ClockBusIndex)
+ BracketClose);
}
}
}
if (byteEnables) {
int NrOfByteEnables = Ram.GetNrOfByteEnables(ComponentInfo
.GetComponent().getAttributeSet());
int ByteEnableOffset = Ram.ByteEnableIndex(ComponentInfo
.GetComponent().getAttributeSet());
for (int i = 0; i < NrOfByteEnables; i++) {
PortMap.putAll(GetNetMap("ByteEnable" + Integer.toString(i),
false, ComponentInfo, ByteEnableOffset
+ NrOfByteEnables - i - 1, Reporter, HDLType,
Nets));
}
}
PortMap.putAll(GetNetMap("DataOut", true, ComponentInfo, Mem.DATA,
Reporter, HDLType, Nets));
return PortMap;
}
@Override
public SortedMap<String, Integer> GetRegList(AttributeSet attrs,
String HDLType) {
SortedMap<String, Integer> Regs = new TreeMap<String, Integer>();
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
int NrOfAddressLines = attrs.getValue(Mem.ADDR_ATTR).getWidth();
Regs.put("s_TickDelayLine", 2);
Regs.put("s_DataInReg", NrOfBits);
Regs.put("s_Address_reg", NrOfAddressLines);
Regs.put("s_WEReg", 1);
Regs.put("s_OEReg", 1);
Regs.put("s_DataOutReg", NrOfBits);
if (byteEnables) {
int NrOfByteEnables = Ram.GetNrOfByteEnables(attrs);
Regs.put("s_ByteEnableReg", NrOfByteEnables);
}
return Regs;
}
@Override
public String GetSubDir() {
return "memory";
}
@Override
public String GetType(int TypeNr) {
switch (TypeNr) {
case MemArrayId:
return MemArrayStr;
case ByteArrayId:
return ByteArrayStr;
case RestArrayId:
return RestArrayStr;
}
return "";
}
@Override
public SortedSet<String> GetTypeDefinitions(Netlist TheNetlist,
AttributeSet attrs, String HDLType) {
SortedSet<String> MyTypes = new TreeSet<String>();
if (HDLType.equals(Settings.VHDL)) {
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
int NrOfAddressLines = attrs.getValue(Mem.ADDR_ATTR).getWidth();
int RamEntries = (1 << NrOfAddressLines);
if (byteEnables) {
MyTypes.add("TYPE " + ByteArrayStr + " IS ARRAY ("
+ Integer.toString(RamEntries - 1)
+ " DOWNTO 0) OF std_logic_vector(7 DOWNTO 0)");
if ((NrOfBits % 8) != 0) {
MyTypes.add("TYPE " + RestArrayStr + " IS ARRAY ("
+ Integer.toString(RamEntries - 1)
+ " DOWNTO 0) OF std_logic_vector("
+ Integer.toString((NrOfBits % 8) - 1)
+ " DOWNTO 0)");
}
} else {
MyTypes.add("TYPE " + MemArrayStr + " IS ARRAY ("
+ Integer.toString(RamEntries - 1)
+ " DOWNTO 0) OF std_logic_vector("
+ Integer.toString(NrOfBits - 1) + " DOWNTO 0)");
}
}
return MyTypes;
}
@Override
public SortedMap<String, Integer> GetWireList(AttributeSet attrs,
Netlist Nets) {
SortedMap<String, Integer> Wires = new TreeMap<String, Integer>();
int NrOfBits = attrs.getValue(Mem.DATA_ATTR).getWidth();
Object be = attrs.getValue(RamAttributes.ATTR_ByteEnables);
boolean byteEnables = be == null ? false : be
.equals(RamAttributes.BUS_WITH_BYTEENABLES);
Wires.put("s_RAM_enable", 1);
Wires.put("s_ram_data_out", NrOfBits);
if (byteEnables) {
for (int i = 0; i < Ram.GetNrOfByteEnables(attrs); i++) {
Wires.put("s_byte_enable_" + Integer.toString(i), 1);
Wires.put("s_we_" + Integer.toString(i), 1);
}
} else {
Wires.put("s_we", 1);
Wires.put("s_oe", 1);
}
return Wires;
}
@Override
public boolean HDLTargetSupported(String HDLType, AttributeSet attrs) {
if (attrs == null)
return false;
Object busVal = attrs.getValue(RamAttributes.ATTR_DBUS);
boolean separate = busVal == null ? false : busVal
.equals(RamAttributes.BUS_SEP);
Object trigger = attrs.getValue(StdAttr.TRIGGER);
boolean asynch = trigger.equals(StdAttr.TRIG_HIGH)
|| trigger.equals(StdAttr.TRIG_LOW);
return HDLType.equals(Settings.VHDL) && separate && !asynch;
}
}