package synthesijer.hdl.expr;
import java.util.ArrayList;
import synthesijer.SynthesijerUtils;
import synthesijer.hdl.HDLExpr;
import synthesijer.hdl.HDLModule;
import synthesijer.hdl.HDLOp;
import synthesijer.hdl.HDLPrimitiveType;
import synthesijer.hdl.HDLSignal;
import synthesijer.hdl.HDLTreeVisitor;
import synthesijer.hdl.HDLType;
public class HDLCombinationExpr implements HDLExpr {
private final int uid;
private final HDLOp op;
private final HDLExpr[] args;
private final HDLSignal result;
public HDLCombinationExpr(HDLModule m, int uid, HDLOp op, HDLExpr... args) {
this.uid = uid;
this.op = op;
this.args = args;
for (HDLExpr expr : args) {
if (expr == null)
throw new RuntimeException("An argument of HDLCombinationExpr is null.");
}
// System.out.println(this);
HDLType type = decideExprType(op, this.args);
result = m.newSignal(String.format("tmp_%04d", uid), type, HDLSignal.ResourceKind.WIRE, this, true);
// System.out.println(result);
}
public HDLType getType() {
return result.getType();
}
public HDLOp getOp() {
return op;
}
private HDLType getPriorType(HDLType t1, HDLType t2) {
if (t1 == null && t2 != null)
return t2;
if (t1 != null && t2 == null)
return t1;
HDLType t = null;
if (t1.getKind().hasWidth() && t1.getKind().isPrimitive() && t2.getKind().hasWidth()
&& t2.getKind().isPrimitive()) {
boolean signFlag = false;
if (t1.isSigned() || t2.isSigned())
signFlag = true;
HDLType tmp = ((HDLPrimitiveType) t1).getWidth() > ((HDLPrimitiveType) t2).getWidth() ? t1 : t2;
if (signFlag) {
t = HDLPrimitiveType.genSignedType(((HDLPrimitiveType) tmp).getWidth());
} else {
t = HDLPrimitiveType.genVectorType(((HDLPrimitiveType) tmp).getWidth());
}
} else if (t1.getKind().hasWidth() && t1.getKind().isPrimitive()) {
t = t1;
} else if (t1.getKind().hasWidth() && t1.getKind().isPrimitive()) {
t = t2;
} else if (t1.getKind() == HDLType.KIND.BIT && t2.getKind() == HDLType.KIND.BIT) {
t = t1;
} else {
}
return t;
}
private String getArgsString(HDLExpr[] args) {
String s = "";
for (HDLExpr a : args) {
s += a.toString() + " ";
}
return s;
}
private HDLType getConcatType(HDLPrimitiveType t0, HDLPrimitiveType t1) {
return HDLPrimitiveType.genVectorType(t0.getWidth() + t1.getWidth());
}
private HDLType getDropHeadType(HDLPrimitiveType t, HDLValue v) {
if (t.isVector()) {
return HDLPrimitiveType.genVectorType(t.getWidth() - Integer.parseInt(v.getValue()));
} else {
return HDLPrimitiveType.genSignedType(t.getWidth() - Integer.parseInt(v.getValue()));
}
}
private HDLType getPaddingHeadType(HDLPrimitiveType t, HDLValue v) {
if (t.isSigned()) {
return HDLPrimitiveType.genSignedType(t.getWidth() + Integer.parseInt(v.getValue()));
} else {
return HDLPrimitiveType.genVectorType(t.getWidth() + Integer.parseInt(v.getValue()));
}
}
private HDLType getTakeType(HDLPrimitiveType t, HDLValue v) {
if (t.isSigned()) {
return HDLPrimitiveType.genSignedType(Integer.parseInt(v.getValue()));
} else {
return HDLPrimitiveType.genVectorType(Integer.parseInt(v.getValue()));
}
}
private HDLType decideExprType(HDLOp op, HDLExpr[] args) {
if (op.isInfix()) {
return getPriorType(args[0].getType(), args[1].getType());
} else if (op.isCompare()) {
return HDLPrimitiveType.genBitType();
} else {
switch (op) {
case NOT:
case MSB_FLAP:
return args[0].getType();
case REF:
return HDLPrimitiveType.genBitType();
case IF:
return getPriorType(args[1].getType(), args[2].getType());
case CONCAT:
return getConcatType((HDLPrimitiveType) args[0].getType(), (HDLPrimitiveType) args[1].getType());
case DROPHEAD:
return getDropHeadType((HDLPrimitiveType) args[0].getType(), (HDLValue) args[1]);
case TAKE:
return getTakeType((HDLPrimitiveType) args[0].getType(), (HDLValue) args[1]);
case PADDINGHEAD:
case PADDINGHEAD_ZERO:
return getPaddingHeadType((HDLPrimitiveType) args[0].getType(), (HDLValue) args[1]);
case ARITH_RSHIFT:
case LOGIC_RSHIFT:
case LSHIFT:
return args[0].getType();
case ARITH_RSHIFT32:
case LOGIC_RSHIFT32:
case LSHIFT32:
return HDLPrimitiveType.genSignedType(32);
case ARITH_RSHIFT64:
case LOGIC_RSHIFT64:
case LSHIFT64:
return HDLPrimitiveType.genSignedType(64);
case ID:
return args[0].getType();
case HDLMUL: {
int w0 = ((HDLPrimitiveType) (args[0].getType())).getWidth();
int w1 = ((HDLPrimitiveType) (args[0].getType())).getWidth();
return HDLPrimitiveType.genSignedType(w0 + w1);
}
default:
return HDLPrimitiveType.genUnknowType();
}
}
}
@Override
public void accept(HDLTreeVisitor v) {
v.visitHDLExpr(this);
}
public String toString() {
return "HDLCombination::(" + op + " " + getArgsString(args) + ")";
}
// TODO experimental code
private String toSigned(HDLExpr expr) {
if (expr instanceof HDLPreDefinedConstant)
return expr.getVHDL();
if (expr instanceof HDLValue)
return expr.getVHDL();
if (expr.getType().isVector()) {
return String.format("signed(%s)", expr.getVHDL());
} else {
return expr.getVHDL();
}
}
// TODO experimental code
private String toStdLogicVector(HDLExpr e) {
String s = e.getResultExpr().getVHDL();
if (e.getType().isSigned()) {
return "std_logic_vector(" + s + ")";
}
return s;
}
private int toImmValue(HDLExpr expr) {
int value = 0;
if (expr instanceof HDLValue) {
value = Integer.parseInt(((HDLValue) expr).getValue());
} else {
value = Integer.parseInt(((HDLValue) ((HDLCombinationExpr) (args[1])).args[0]).getValue());
}
return value;
}
@Override
public String getVHDL() {
boolean arith_shift_mode = false;
if (op.isInfix()) {
String s = String.format("%s %s %s", toSigned(args[0].getResultExpr()), op.getVHDL(),
toSigned(args[1].getResultExpr()));
if (getResultExpr().getType().isVector())
s = "std_logic_vector(" + s + ")";
return s;
} else if (op.isCompare()) {
if (args[0] instanceof HDLValue && args[0].getType().isBit()) {
if(args[0].getVHDL().equals("\'1\'")){
if(op == HDLOp.EQ) return args[1].getResultExpr().getVHDL();
if(op == HDLOp.NEQ) return String.format("not %s", args[1].getResultExpr().getVHDL());
}else if(args[0].getVHDL().equals("\'0\'")){
if(op == HDLOp.EQ) return String.format("not %s", args[1].getResultExpr().getVHDL());
if(op == HDLOp.NEQ) return args[1].getResultExpr().getVHDL();
}else{
SynthesijerUtils.warn("constant bit operation will be error:" + this);
}
}
return String.format("'1' when %s %s %s else '0'", toSigned(args[0].getResultExpr()), op.getVHDL(),
toSigned(args[1].getResultExpr()));
} else {
switch (op) {
case NOT:
return String.format("%s %s", op.getVHDL(), args[0].getResultExpr().getVHDL());
case MSB_FLAP: {
String v = args[0].getResultExpr().getVHDL();
HDLPrimitiveType t = (HDLPrimitiveType) (args[0].getResultExpr().getType());
return String.format("(not %s(%d-1)) & %s(%d-2 downto 0)", v, t.getWidth(), v, t.getWidth());
}
case REF:
return String.format("%s(%s)", args[0].getResultExpr().getVHDL(), args[1].getResultExpr().getVHDL());
case IF: {
HDLType t = getResultExpr().getType();
String arg1, arg2;
arg1 = (t.isSigned() && args[1].getResultExpr().getType().isVector())
? toSigned(args[1].getResultExpr()) : args[1].getResultExpr().getVHDL();
arg2 = (t.isSigned() && args[2].getResultExpr().getType().isVector())
? toSigned(args[2].getResultExpr()) : args[2].getResultExpr().getVHDL();
String r = String.format("%s when %s = '1' else %s", arg1, args[0].getResultExpr().getVHDL(), arg2);
return r;
}
case CONCAT: {
String arg0 = toStdLogicVector(args[0].getResultExpr());
String arg1 = toStdLogicVector(args[1].getResultExpr());
return String.format("%s & %s", arg0, arg1);
}
case DROPHEAD: {
HDLPrimitiveType t = (HDLPrimitiveType) args[0].getResultExpr().getType();
return String.format("%s(%d - %s - 1 downto 0)", args[0].getResultExpr().getVHDL(), t.getWidth(),
((HDLValue) args[1]).getValue());
}
case TAKE: {
HDLPrimitiveType t = (HDLPrimitiveType) getResultExpr().getType();
return String.format("%s(%d - 1 downto 0)", args[0].getResultExpr().getVHDL(), t.getWidth());
}
case PADDINGHEAD: {
HDLPrimitiveType t0 = (HDLPrimitiveType) args[0].getResultExpr().getType();
HDLPrimitiveType t1 = (HDLPrimitiveType) decideExprType(op, args);
return String.format("(%d-1 downto %d => %s(%d)) & %s", t1.getWidth(), t0.getWidth(),
args[0].getResultExpr().getVHDL(), t0.getWidth() - 1, args[0].getResultExpr().getVHDL());
}
case PADDINGHEAD_ZERO: {
HDLPrimitiveType t0 = (HDLPrimitiveType) args[0].getResultExpr().getType();
HDLPrimitiveType t1 = (HDLPrimitiveType) decideExprType(op, args);
return String.format("(%d-1 downto %d => '0') & %s", t1.getWidth(), t0.getWidth(),
args[0].getResultExpr().getVHDL(), t0.getWidth() - 1, args[0].getResultExpr().getVHDL());
}
case ARITH_RSHIFT:
case ARITH_RSHIFT32:
case ARITH_RSHIFT64:
arith_shift_mode = true; // update
case LOGIC_RSHIFT:
case LOGIC_RSHIFT32:
case LOGIC_RSHIFT64: {
String str;
HDLPrimitiveType t0 = (HDLPrimitiveType) result.getType();
int shift = toImmValue(args[1]);
if (args[0] instanceof HDLValue) {
int value = toImmValue(args[0]);
HDLValue tmp = arith_shift_mode
? new HDLValue(String.valueOf(value >> shift), (HDLPrimitiveType) result.getType())
: new HDLValue(String.valueOf(value >>> shift), (HDLPrimitiveType) result.getType());
str = tmp.getVHDL();
}
HDLPrimitiveType ta = (HDLPrimitiveType) (args[0].getResultExpr().getType());
String padding = arith_shift_mode
? String.format("%s(%d)", args[0].getResultExpr().getVHDL(), ta.getWidth() - 1) : "'0'";
// int msb = t0.getWidth()-shift-1;
int msb = t0.getWidth() - 1;
// if(msb > ta.getWidth()-1) msb = ta.getWidth()-1;
int lsb = shift;
if (lsb > ta.getWidth() - 1)
lsb = 0;
if (shift >= ta.getWidth()) {
//str = String.format("(%d-1 downto 0 => %s)", t0.getWidth(), padding);
str = args[0].getResultExpr().getVHDL(); // as is
} else if (shift >= 1 && shift < ta.getWidth()) {
str = String.format("(%d-1 downto %d => %s) & %s(%d downto %d)", t0.getWidth(), (msb - lsb + 1), // shift,
padding, args[0].getResultExpr().getVHDL(), msb, lsb);
} else { // shift == 0
if (t0.getWidth() == ta.getWidth()) { // as is
str = args[0].getResultExpr().getVHDL();
} else {
str = String.format("(%d-1 downto %d => %s) & %s", t0.getWidth(), msb + 1, padding,
args[0].getResultExpr().getVHDL());
}
}
return str;
}
case LSHIFT:
case LSHIFT32:
case LSHIFT64: {
HDLPrimitiveType t0 = (HDLPrimitiveType) (result.getType());
int shift = toImmValue(args[1]);
if (args[0] instanceof HDLValue) {
int value = toImmValue(args[0]);
HDLValue tmp = new HDLValue(String.valueOf(value << shift), (HDLPrimitiveType) result.getType());
return tmp.getVHDL();
}
HDLPrimitiveType ta = (HDLPrimitiveType) (args[0].getResultExpr().getType());
int msb = t0.getWidth() - shift - 1;
if (msb > ta.getWidth() - 1)
msb = ta.getWidth() - 1;
String str = "";
if (shift >= ta.getWidth()) {
str = args[0].getResultExpr().getVHDL(); // as is
}else if (shift >= 1) {
str += String.format("%s(%d downto %d) & (%d-1 downto %d => '0')",
args[0].getResultExpr().getVHDL(), msb, 0, shift, 0);
} else {
str += args[0].getResultExpr().getVHDL();
}
if (msb + shift < t0.getWidth() - 1) {
str = String.format("(%d downto %d => '0') & ", t0.getWidth() - 1, msb + shift + 1) + str;
}
return str;
}
case ID:
return args[0].getResultExpr().getVHDL();
case HDLMUL: {
String s = String.format("%s %s %s", toSigned(args[0].getResultExpr()), op.getVHDL(),
toSigned(args[1].getResultExpr()));
if (getResultExpr().getType().isVector())
s = "std_logic_vector(" + s + ")";
return s;
}
default:
return "(" + op + " " + getArgsString(args) + ")";
}
}
}
private String getPaddingBitInVerilog(String key, int idx, int len) {
String s = "";
String sep = "";
for (int i = 0; i < len; i++) {
s += sep + key + "[" + idx + "]";
sep = ",";
}
return s;
}
@Override
public String getVerilogHDL() {
boolean arith_shift_mode = false;
if (op.isInfix()) {
return String.format("%s %s %s", args[0].getResultExpr().getVerilogHDL(), op.getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL());
} else if (op.isCompare()) {
return String.format("%s %s %s ? 1'b1 : 1'b0", args[0].getResultExpr().getVerilogHDL(), op.getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL());
} else {
switch (op) {
case NOT:
return String.format("%s%s", op.getVerilogHDL(), args[0].getResultExpr().getVerilogHDL());
case MSB_FLAP: {
String v = args[0].getResultExpr().getVerilogHDL();
HDLPrimitiveType t = (HDLPrimitiveType) (args[0].getResultExpr().getType());
return String.format("{(~%s[%d-1]), %s[%d-2:0]}", v, t.getWidth(), v, t.getWidth());
}
case REF:
return String.format("%s[%s]", args[0].getResultExpr().getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL());
case IF:
return String.format("%s == 1'b1 ? %s : %s", args[0].getResultExpr().getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL(), args[2].getResultExpr().getVerilogHDL());
case CONCAT:
return String.format("{%s, %s}", args[0].getResultExpr().getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL());
case DROPHEAD: {
HDLPrimitiveType t = (HDLPrimitiveType) args[0].getResultExpr().getType();
return String.format("%s[%d - %s - 1 : 0]", args[0].getResultExpr().getVerilogHDL(), t.getWidth(),
args[1].getResultExpr().getVerilogHDL());
}
case TAKE: {
HDLPrimitiveType t = (HDLPrimitiveType) getResultExpr().getType();
return String.format("%s[%d - 1 : 0]", args[0].getResultExpr().getVerilogHDL(), t.getWidth());
}
case PADDINGHEAD: {
HDLPrimitiveType t0 = (HDLPrimitiveType) args[0].getResultExpr().getType();
HDLPrimitiveType t1 = (HDLPrimitiveType) decideExprType(op, args);
return String.format("{%s,%s}", getPaddingBitInVerilog(args[0].getResultExpr().getVerilogHDL(),
t0.getWidth() - 1, t1.getWidth() - t0.getWidth()), args[0].getResultExpr().getVerilogHDL());
}
case PADDINGHEAD_ZERO: {
HDLPrimitiveType t0 = (HDLPrimitiveType) args[0].getResultExpr().getType();
HDLPrimitiveType t1 = (HDLPrimitiveType) decideExprType(op, args);
return String.format("{%d'b0, %s}", t1.getWidth() - t0.getWidth(),
args[0].getResultExpr().getVerilogHDL());
}
case ARITH_RSHIFT:
case ARITH_RSHIFT32:
case ARITH_RSHIFT64:
arith_shift_mode = true; // overwrite
case LOGIC_RSHIFT:
case LOGIC_RSHIFT32:
case LOGIC_RSHIFT64: {
String str;
HDLPrimitiveType t0 = (HDLPrimitiveType) result.getType();
int shift = toImmValue(args[1]);
if (args[0] instanceof HDLValue) {
int value = toImmValue(args[0]);
HDLValue tmp = arith_shift_mode
? new HDLValue(String.valueOf(value >> shift), (HDLPrimitiveType) result.getType())
: new HDLValue(String.valueOf(value >>> shift), (HDLPrimitiveType) result.getType());
str = tmp.getVerilogHDL();
}
HDLPrimitiveType ta = (HDLPrimitiveType) (args[0].getResultExpr().getType());
// int msb = t0.getWidth()-shift-1;
// if(msb > ta.getWidth()-1) msb = ta.getWidth()-1;
int msb = t0.getWidth() - 1;
int lsb = shift;
if (lsb > ta.getWidth() - 1)
lsb = 0;
String key = args[0].getResultExpr().getVerilogHDL();
if (shift >= ta.getWidth()) {
str = args[0].getResultExpr().getVerilogHDL(); // as is
/*
if (arith_shift_mode) {
str = String.format("{%s}", getPaddingBitInVerilog(key, ta.getWidth() - 1, t0.getWidth()));
} else {
str = String.format("%d'b0", t0.getWidth());
}
*/
} else if (shift >= 1 && shift < ta.getWidth()) {
String pad;
String val = String.format("%s[%d:%d]", key, msb, lsb);
if (arith_shift_mode) {
pad = getPaddingBitInVerilog(key, ta.getWidth() - 1, t0.getWidth() - (msb - lsb + 1));
} else {
pad = String.format("%d'b0", t0.getWidth() - (msb - lsb + 1));
}
str = String.format("{%s,%s}", pad, val);
} else { // shift == 0
if (t0.getWidth() == ta.getWidth()) { // as is
str = key;
} else {
String pad;
if (arith_shift_mode) {
pad = getPaddingBitInVerilog(key, ta.getWidth() - 1, t0.getWidth() - (msb - lsb + 1));
} else {
pad = String.format("%d'b0", t0.getWidth() - (msb - lsb));
}
str = String.format("{%s,%s}", pad, key);
}
}
return str;
}
case LSHIFT:
case LSHIFT32:
case LSHIFT64: {
// HDLPrimitiveType t0 =
// (HDLPrimitiveType)args[0].getResultExpr().getType();
HDLPrimitiveType t0 = (HDLPrimitiveType) (result.getType());
// int shift =
// Integer.parseInt(((HDLValue)((HDLCombinationExpr)(args[1])).args[0]).getValue());
int shift = toImmValue(args[1]);
if (args[0] instanceof HDLValue) {
int value = toImmValue(args[0]);
// HDLValue tmp = new HDLValue(String.valueOf(value <<
// shift), (HDLPrimitiveType)args[0].getType());
HDLValue tmp = new HDLValue(String.valueOf(value << shift), (HDLPrimitiveType) result.getType());
return tmp.getVerilogHDL();
}
HDLPrimitiveType ta = (HDLPrimitiveType) (args[0].getResultExpr().getType());
int msb = t0.getWidth() - shift - 1;
if (msb > ta.getWidth() - 1)
msb = ta.getWidth() - 1;
String str = "";
if (shift >= ta.getWidth()) {
str = args[0].getResultExpr().getVerilogHDL(); // as is
}else if (shift >= 1) {
str = String.format("%s[%d:%d],%d'b0", args[0].getResultExpr().getVerilogHDL(),
t0.getWidth() - shift - 1, 0, shift);
} else {
str = args[0].getResultExpr().getVerilogHDL();
}
if (msb + shift < t0.getWidth() - 1) {
str = String.format("%d'b0,", t0.getWidth() - (msb + shift) - 1) + str;
}
str = "{" + str + "}";
return str;
}
case ID:
return args[0].getResultExpr().getVerilogHDL();
case HDLMUL:
return String.format("%s %s %s", args[0].getResultExpr().getVerilogHDL(), op.getVerilogHDL(),
args[1].getResultExpr().getVerilogHDL());
default:
return "(" + op + " " + getArgsString(args) + ")";
}
}
}
@Override
public HDLExpr getResultExpr() {
return result;
}
private void getSrcSignals(ArrayList<HDLSignal> list, HDLExpr arg) {
HDLSignal[] src = arg.getSrcSignals();
if (src != null) {
for (HDLSignal s : src) {
list.add(s);
}
}
if (arg.getResultExpr() instanceof HDLSignal) {
list.add((HDLSignal) arg.getResultExpr());
}
}
@Override
public HDLSignal[] getSrcSignals() {
ArrayList<HDLSignal> list = new ArrayList<>();
for (HDLExpr arg : args) {
getSrcSignals(list, arg);
}
return list.toArray(new HDLSignal[] {});
}
}