/*******************************************************************************
* 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.bfh.logisim.download;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import com.bfh.logisim.designrulecheck.Netlist;
import com.bfh.logisim.fpgaboardeditor.BoardInformation;
import com.bfh.logisim.fpgaboardeditor.FPGAClass;
import com.bfh.logisim.fpgaboardeditor.PullBehaviors;
import com.bfh.logisim.fpgagui.FPGAReport;
import com.bfh.logisim.fpgagui.MappableResourcesContainer;
import com.bfh.logisim.hdlgenerator.FileWriter;
import com.bfh.logisim.hdlgenerator.TickComponentHDLGeneratorFactory;
import com.bfh.logisim.hdlgenerator.ToplevelHDLGeneratorFactory;
import com.bfh.logisim.settings.Settings;
import com.bfh.logisim.settings.VendorSoftware;
import com.cburch.logisim.proj.Projects;
public class AlteraDownload {
public static boolean Download(Settings MySettings, String scriptPath,
String ProjectPath, String SandboxPath, FPGAReport MyReporter) {
VendorSoftware alteraVendor = Settings.vendors.get(FPGAClass.VendorAltera);
boolean SofFileExists = new File(SandboxPath
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + ".sof")
.exists();
GridBagConstraints gbc = new GridBagConstraints();
JFrame panel = new JFrame("Altera Downloading");
panel.setResizable(false);
panel.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
GridBagLayout thisLayout = new GridBagLayout();
panel.setLayout(thisLayout);
// PointerInfo mouseloc = MouseInfo.getPointerInfo();
// Point mlocation = mouseloc.getLocation();
// panel.setLocation(mlocation.x, mlocation.y);
JLabel LocText = new JLabel(
"Generating FPGA files and performing download; this may take a while");
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(LocText, gbc);
JProgressBar progres = new JProgressBar(0, 5);
progres.setValue(1);
progres.setStringPainted(true);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(progres, gbc);
panel.pack();
panel.setLocation(Projects.getCenteredLoc(panel.getWidth(),
panel.getHeight() * 4));
panel.setVisible(true);
Rectangle labelRect = LocText.getBounds();
labelRect.x = 0;
labelRect.y = 0;
LocText.paintImmediately(labelRect);
List<String> command = new ArrayList<String>();
if (!SofFileExists) {
try {
LocText.setText("Creating Project");
labelRect = LocText.getBounds();
labelRect.x = 0;
labelRect.y = 0;
LocText.paintImmediately(labelRect);
command.add(alteraVendor.getBinaryPath(0));
command.add("-t");
command.add(scriptPath.replace(ProjectPath, ".."
+ File.separator)
+ "AlteraDownload.tcl");
ProcessBuilder Altera1 = new ProcessBuilder(command);
Altera1.directory(new File(SandboxPath));
final Process CreateProject = Altera1.start();
InputStream is = CreateProject.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
MyReporter.ClsScr();
while ((line = br.readLine()) != null) {
MyReporter.print(line);
}
CreateProject.waitFor();
if (CreateProject.exitValue() != 0) {
MyReporter
.AddFatalError("Failed to Create a Quartus Project, cannot download");
panel.dispose();
return false;
}
} catch (IOException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
} catch (InterruptedException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
}
}
progres.setValue(2);
Rectangle ProgRect = progres.getBounds();
ProgRect.x = 0;
ProgRect.y = 0;
progres.paintImmediately(ProgRect);
command.clear();
if (!SofFileExists) {
try {
LocText.setText("Optimize Project");
labelRect = LocText.getBounds();
labelRect.x = 0;
labelRect.y = 0;
LocText.paintImmediately(labelRect);
command.add(alteraVendor.getBinaryPath(2));
command.add(ToplevelHDLGeneratorFactory.FPGAToplevelName);
command.add("--optimize=area");
ProcessBuilder Altera1 = new ProcessBuilder(command);
Altera1.directory(new File(SandboxPath));
final Process CreateProject = Altera1.start();
InputStream is = CreateProject.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
MyReporter.ClsScr();
while ((line = br.readLine()) != null) {
MyReporter.print(line);
}
CreateProject.waitFor();
if (CreateProject.exitValue() != 0) {
MyReporter
.AddFatalError("Failed to optimize (AREA) Project, cannot download");
panel.dispose();
return false;
}
} catch (IOException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
} catch (InterruptedException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
}
}
LocText.setText("Synthesizing and creating configuration file (this may take a while)");
labelRect = LocText.getBounds();
labelRect.x = 0;
labelRect.y = 0;
LocText.paintImmediately(labelRect);
progres.setValue(3);
ProgRect = progres.getBounds();
ProgRect.x = 0;
ProgRect.y = 0;
progres.paintImmediately(ProgRect);
if (!SofFileExists) {
try {
command.clear();
command.add(alteraVendor.getBinaryPath(0));
command.add("--flow");
command.add("compile");
command.add(ToplevelHDLGeneratorFactory.FPGAToplevelName);
ProcessBuilder Altera1 = new ProcessBuilder(command);
Altera1.directory(new File(SandboxPath));
final Process CreateProject = Altera1.start();
InputStream is = CreateProject.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
MyReporter.ClsScr();
while ((line = br.readLine()) != null) {
MyReporter.print(line);
}
CreateProject.waitFor();
if (CreateProject.exitValue() != 0) {
MyReporter
.AddFatalError("Failed to synthesize design and to create the configuration files, cannot download");
panel.dispose();
return false;
}
} catch (IOException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
} catch (InterruptedException e) {
MyReporter
.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
}
}
LocText.setText("Downloading");
Object[] options = { "Yes, download","No, abort" };
if (JOptionPane
.showOptionDialog(
progres,
"Verify that your board is connected and you are ready to download.",
"Ready to download ?", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE, null, options, options[0]) != JOptionPane.YES_OPTION) {
MyReporter.AddWarning("Download aborted.");
panel.dispose();
return false;
}
labelRect = LocText.getBounds();
labelRect.x = 0;
labelRect.y = 0;
LocText.paintImmediately(labelRect);
progres.setValue(4);
ProgRect = progres.getBounds();
ProgRect.x = 0;
ProgRect.y = 0;
progres.paintImmediately(ProgRect);
try {
command.clear();
command.add(alteraVendor.getBinaryPath(1));
command.add("-c");
command.add("usb-blaster");
command.add("-m");
command.add("jtag");
command.add("-o");
// if there is no .sof generated, try with the .pof
if (new File(SandboxPath
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + ".sof")
.exists()) {
command.add("P;" + ToplevelHDLGeneratorFactory.FPGAToplevelName
+ ".sof");
} else {
command.add("P;" + ToplevelHDLGeneratorFactory.FPGAToplevelName
+ ".pof");
}
MyReporter.AddInfo(command.toString());
ProcessBuilder Altera1 = new ProcessBuilder(command);
Altera1.directory(new File(SandboxPath));
final Process CreateProject = Altera1.start();
InputStream is = CreateProject.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
MyReporter.ClsScr();
while ((line = br.readLine()) != null) {
MyReporter.print(line);
}
CreateProject.waitFor();
if (CreateProject.exitValue() != 0) {
MyReporter
.AddFatalError("Failed to Download design; did you connect the board?");
panel.dispose();
return false;
}
} catch (IOException e) {
MyReporter.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
} catch (InterruptedException e) {
MyReporter.AddFatalError("Internal Error during Altera download");
panel.dispose();
return false;
}
panel.dispose();
return true;
}
public static boolean GenerateQuartusScript(FPGAReport MyReporter,
String ScriptPath, Netlist RootNetList,
MappableResourcesContainer MapInfo, BoardInformation BoardInfo,
ArrayList<String> Entities, ArrayList<String> Architectures,
String HDLType) {
File ScriptFile = FileWriter.GetFilePointer(ScriptPath,
"AlteraDownload.tcl", MyReporter);
if (ScriptFile == null) {
ScriptFile = new File(ScriptPath + "AlteraDownload.tcl");
return ScriptFile.exists();
}
String FileType = (HDLType.equals(Settings.VHDL)) ? "VHDL_FILE"
: "VERILOG_FILE";
ArrayList<String> Contents = new ArrayList<String>();
Contents.add("# Load Quartus II Tcl Project package");
Contents.add("package require ::quartus::project");
Contents.add("");
Contents.add("set need_to_close_project 0");
Contents.add("set make_assignments 1");
Contents.add("");
Contents.add("# Check that the right project is open");
Contents.add("if {[is_project_open]} {");
Contents.add(" if {[string compare $quartus(project) \""
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + "\"]} {");
Contents.add(" puts \"Project "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName
+ " is not open\"");
Contents.add(" set make_assignments 0");
Contents.add(" }");
Contents.add("} else {");
Contents.add(" # Only open if not already open");
Contents.add(" if {[project_exists "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + "]} {");
Contents.add(" project_open -revision "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + " "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName);
Contents.add(" } else {");
Contents.add(" project_new -revision "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName + " "
+ ToplevelHDLGeneratorFactory.FPGAToplevelName);
Contents.add(" }");
Contents.add(" set need_to_close_project 1");
Contents.add("}");
Contents.add("# Make assignments");
Contents.add("if {$make_assignments} {");
Contents.addAll(GetAlteraAssignments(BoardInfo));
Contents.add("");
Contents.add(" # Include all entities and gates");
Contents.add("");
for (int i = 0; i < Entities.size(); i++) {
Contents.add(" set_global_assignment -name " + FileType + " \""
+ Entities.get(i) + "\"");
}
for (int i = 0; i < Architectures.size(); i++) {
Contents.add(" set_global_assignment -name " + FileType + " \""
+ Architectures.get(i) + "\"");
}
Contents.add("");
Contents.add(" # Map fpga_clk and ionets to fpga pins");
if (RootNetList.NumberOfClockTrees() > 0) {
Contents.add(" set_location_assignment "
+ BoardInfo.fpga.getClockPinLocation() + " -to "
+ TickComponentHDLGeneratorFactory.FPGAClock);
}
Contents.addAll(MapInfo.GetFPGAPinLocs(FPGAClass.VendorAltera));
Contents.add(" # Commit assignments");
Contents.add(" export_assignments");
Contents.add("");
Contents.add(" # Close project");
Contents.add(" if {$need_to_close_project} {");
Contents.add(" project_close");
Contents.add(" }");
Contents.add("}");
return FileWriter.WriteContents(ScriptFile, Contents, MyReporter);
}
private static ArrayList<String> GetAlteraAssignments(
BoardInformation CurrentBoard) {
ArrayList<String> result = new ArrayList<String>();
String Assignment = " set_global_assignment -name ";
result.add(Assignment + "FAMILY \"" + CurrentBoard.fpga.getTechnology()
+ "\"");
result.add(Assignment + "DEVICE " + CurrentBoard.fpga.getPart());
String[] Package = CurrentBoard.fpga.getPackage().split(" ");
result.add(Assignment + "DEVICE_FILTER_PACKAGE " + Package[0]);
result.add(Assignment + "DEVICE_FILTER_PIN_COUNT " + Package[1]);
if (CurrentBoard.fpga.getUnusedPinsBehavior() == PullBehaviors.Float) {
result.add(Assignment
+ "RESERVE_ALL_UNUSED_PINS \"AS INPUT TRI-STATED\"");
}
if (CurrentBoard.fpga.getUnusedPinsBehavior() == PullBehaviors.PullUp) {
result.add(Assignment
+ "RESERVE_ALL_UNUSED_PINS \"AS INPUT PULLUP\"");
}
if (CurrentBoard.fpga.getUnusedPinsBehavior() == PullBehaviors.PullDown) {
result.add(Assignment
+ "RESERVE_ALL_UNUSED_PINS \"AS INPUT PULLDOWN\"");
}
result.add(Assignment + "FMAX_REQUIREMENT \""
+ GetClockFrequencyString(CurrentBoard) + "\"");
result.add(Assignment + "RESERVE_NCEO_AFTER_CONFIGURATION \"USE AS REGULAR IO\"");
result.add(Assignment + "CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION \"USE AS REGULAR IO\"");
return result;
}
private static String GetClockFrequencyString(BoardInformation CurrentBoard) {
long clkfreq = CurrentBoard.fpga.getClockFrequency();
if (clkfreq % 1000000 == 0) {
clkfreq /= 1000000;
return Long.toString(clkfreq) + " MHz ";
} else if (clkfreq % 1000 == 0) {
clkfreq /= 1000;
return Long.toString(clkfreq) + " kHz ";
}
return Long.toString(clkfreq);
}
}