-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCombinationalCircuitForm.cs
543 lines (434 loc) · 18.1 KB
/
CombinationalCircuitForm.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
using Noesis.Javascript;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// TODO: There is a bug where changing the name of the combinational system erases the CurrentSystem.
// The system should probably only be erased when the number of inputs or outputs changes.
namespace BooleDeustoTwo
{
public partial class CombinationalCircuitForm : Form
{
/// <summary>
/// This property is meant to hold the current full digital system.
/// The system it holds is "full" in the sense that it is well-defined
/// and could hence, theoretically, be generated into valid VHDL code.
/// As of now, a full system info will contain the input and output names,
/// and the values for every output, in order. It's a JSON-serializable object, whose
/// format is described throughout the code.
/// It should contain items only. That is, JsonObject instead of dictionaries, and
/// JsonArrays instead of lists.
/// TODO: We should probably make sure that CurrentSystem is set to null every time
/// the input/output table is modified, so that the CurrentSystem always matches
/// the one displayed.
/// </summary>
dynamic CurrentSystem
{
get;
set;
}
public CombinationalCircuitForm()
{
InitializeComponent();
}
private void exitButton_Click(object sender, EventArgs e)
{
Application.Exit();
}
/// <summary>
/// Serializes the system to a dynamic object which can be directly
/// serialized into JSON.
/// </summary>
/// <returns>Dynamic object with data to describe the system</returns>
private dynamic SerializeSystem()
{
dynamic sys = new JsonObject();
sys["name"] = nameBox.Text;
var inputs = new List<string>();
int i = 0;
foreach (DataGridViewRow row in inputsGrid.Rows)
{
string name;
var val = row.Cells[0].Value;
if (val != null)
name = row.Cells[0].Value.ToString();
else
name = ((char)('A' + i)).ToString();
inputs.Add(name);
i++;
}
var outputs = new JsonArray();
i = 1;
foreach (DataGridViewRow row in outputsGrid.Rows)
{
string name;
var val = row.Cells[0].Value;
if (val != null)
name = row.Cells[0].Value.ToString();
else
name = "F" + i.ToString();
outputs.Add(name);
i++;
}
sys["inputs"] = inputs;
sys["outputs"] = outputs;
return sys;
}
/// <summary>
/// Saves the system to a file.
/// BooleDeusto2 files end in bd2, and are JSON-based.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void saveButton_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "BooleDeusto2 Files (*.bd2)|*.bd2";
sfd.ShowDialog();
string file = sfd.FileName;
if (file != null && file.Length > 0)
{
// If the system is well-defined and up to date, then we use it. Otherwise we serialize the
// currently displayed system on the GUI, which won't have values set for the outputs.
dynamic sys = CurrentSystem != null? CurrentSystem : SerializeSystem();
string json = SimpleJson.SerializeObject(sys);
File.WriteAllText(file, json);
}
}
/// <summary>
/// Deserializes from a dynamic object describing the system.
/// This method is analogue to SerializeSystem.
/// </summary>
/// <param name="sys">Dynamic object to extract the data from.</param>
private void DeserializeSystem(dynamic sys)
{
this.nameBox.Text = sys["name"];
inputsGrid.Rows.Clear();
foreach (var input in sys["inputs"])
inputsGrid.Rows.Add(input);
outputsGrid.Rows.Clear();
foreach (var output in sys["outputs"])
outputsGrid.Rows.Add(output);
outputsNumber.Value = sys["outputs"].Count;
inputsNumber.Value = sys["inputs"].Count;
}
/// <summary>
/// Loads the system from a file.
/// BooleDeusto2 files are JSON-based.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void loadButton_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "BooleDeusto2 Files (*.bd2)|*.bd2";
ofd.ShowDialog();
string file = ofd.FileName;
if (file == null || file.Length == 0)
return;
string json = File.ReadAllText(file);
dynamic sys = SimpleJson.DeserializeObject(json);
DeserializeSystem(sys);
// TODO: There is currently a major issue with this. The sys object isn't uniformly built,
// as sometimes a Dictionary is used etc. The interface to check whether a key exists seems
// to be different. That issue will need to be solved before this can work properly.
if (sys.ContainsKey("outputValues"))
{
// If we're dealing with a full system, it becomes our new CurrentSystem.
CurrentSystem = sys;
}
}
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
class SystemConsole
{
public void Print(string str)
{
Console.WriteLine(str);
}
}
private void test_Click(object sender, EventArgs e)
{
// Initialize a context
JavascriptContext context = new JavascriptContext();
// Setting external parameters for the context
context.SetParameter("console", new SystemConsole());
context.SetParameter("message", "Hello World !");
context.SetParameter("number", 1);
// Script
string script = @"
var i;
for (i = 0; i < 5; i++)
console.Print(message + ' (' + i + ')');
number += i;
";
// Running the script
context.Run(script);
// Getting a parameter
Console.WriteLine("number: " + context.GetParameter("number"));
}
private void systemPropertiesTab_Click(object sender, EventArgs e)
{
}
private void onInputsNumberChanged(object sender, EventArgs e)
{
int rows = inputsGrid.Rows.Count;
int target = (int)inputsNumber.Value;
if (target > rows)
{
inputsGrid.Rows.Add(target - rows);
NameInputs();
}
else if (target < rows)
RemoveRows(inputsGrid, rows - target);
}
private void onOutputsNumberChanged(object sender, EventArgs e)
{
int rows = outputsGrid.Rows.Count;
int target = (int)outputsNumber.Value;
if (target > rows)
{
outputsGrid.Rows.Add(target - rows);
NameOutputs();
}
else if (target < rows)
RemoveRows(outputsGrid, rows - target);
}
/// <summary>
/// Sets the ID of every single input. Inputs are identified by a letter,
/// starting from A.
/// </summary>
private void NameInputs()
{
char nextLetter = (char)'A';
for (int i = 0; i < inputsGrid.Rows.Count; i++)
inputsGrid.Rows[i].HeaderCell.Value = (nextLetter++).ToString();
}
/// <summary>
/// Sets the ID of every single output. Outputs are identified by 'F' plus a number,
/// starting from F1.
/// </summary>
private void NameOutputs()
{
int nextNum = 1;
for (int i = 0; i < outputsGrid.Rows.Count; i++)
outputsGrid.Rows[i].HeaderCell.Value = "F" + (nextNum++).ToString();
}
private void RemoveRows(DataGridView grid, int num)
{
for (int i = 0; i < num; i++ )
grid.Rows.RemoveAt(grid.Rows.Count - 1);
}
private void onFormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
private void onFormLoad(object sender, EventArgs e)
{
this.nameBox.Text = "Unnamed";
}
/// <summary>
/// This is the method to call whenever the inputs or outputs were changed.
/// It means that the CurrentSystem is no longer up to date, and is thus cleared.
/// </summary>
private void onInputsOutputsChangeOccurred()
{
CurrentSystem = null;
}
private void completeTruthTableButton_Click(object sender, EventArgs e)
{
CompleteTruthTableForm ttf = new CompleteTruthTableForm();
dynamic sys;
// If we have the system already, we do not need to serialize it from the GUI.
if(CurrentSystem != null)
sys = CurrentSystem;
else
sys = SerializeSystem();
if (sys["inputs"].Count == 0)
{
MessageBox.Show("System has not been defined yet");
return;
}
ttf.LoadSystem(sys);
ttf.ShowDialog();
// Check whether the outputs are now set, so that we can update the system accordingly.
if (ttf.OutputValues != null)
{
// We update the system
sys["outputValues"] = ttf.OutputValues;
CurrentSystem = sys;
}
}
private void OnCellValidated(object sender, DataGridViewCellEventArgs e)
{
// This event is probably thrown more often than we need for our purpose, but it should work fine,
// especially on an initial stage.
// We want to remove the CurrentSystem whenever something in the inputs or outputs has changed, because
// it means that whatever outputs were defined for the previous system, they no longer apply.
// Here we make sure that is indeed done.
this.onInputsOutputsChangeOccurred();
}
/// <summary>
/// Extracts a column of outputs from the CurrentSystem.
/// </summary>
/// <param name="col">Column to extract. For instance, 0 to extract the first outputs column.</param>
/// <returns></returns>
private List<String> ExtractOutputs(int col)
{
// Extracts column of outputs
var outputs = new List<String>();
int outputNum = col;
int i = 0;
int n = 0;
foreach (string output in CurrentSystem["outputValues"])
{
if (i % CurrentSystem["outputs"].Count == outputNum)
outputs.Add(output);
i++;
}
return outputs;
}
private List<string> GenerateSOPs(string format = "booleanAlgebra")
{
// To store the sops we will generate.
List<string> sops = new List<string>();
// Ensure that our system is fully defined. Otherwise we
// can't generate SOP expressions.
if (CurrentSystem == null || !CurrentSystem.ContainsKey("outputValues"))
return sops;
// Initialize a context
JavascriptContext context = new JavascriptContext();
// Load Source of the QM algorithm script.
string source = File.ReadAllText("../../js/quine/src/qm.js");
context.Run(source);
// Setting external parameters for the context
context.SetParameter("console", new SystemConsole());
// Convert inputs to a comma-separated string
string inputsStr = string.Join(",", CurrentSystem["inputs"]);
var minterms = new List<string>();
var dontNeeds = new List<string>();
// We will need to obtain a boolean equation for each output.
for (int outputNum = 0; outputNum < CurrentSystem["outputs"].Count; outputNum++)
{
// Generate minterms and dontNeeds list
int i = 0;
foreach (string output in ExtractOutputs(outputNum))
{
if (output == "1")
minterms.Add(i.ToString());
else if (output == "X")
dontNeeds.Add(i.ToString());
i++;
}
string mintermsStr = string.Join(",", minterms);
string dontNeedsStr = string.Join(",", dontNeeds);
// Script
string script = string.Format(@"
var userInput = {{ inputs: '{0}', minterms: '{1}', dontNeeds: '{2}' }};
result = qm.getLeastPrimeImplicants( userInput, '{3}' );
", inputsStr, mintermsStr, dontNeedsStr, format);
// Running the script
context.Run(script);
// Extract the resulting equation
string eq = context.GetParameter("result").ToString();
// Add the SOP we have just obtained to our list of SOPs.
sops.Add(eq);
//MessageBox.Show(eq);
// Getting a parameter
Console.WriteLine("number: " + context.GetParameter("number"));
}
return sops;
}
private void sopButton_Click(object sender, EventArgs e)
{
// Ensure that our system is fully defined. Otherwise we
// can't generate SOP expressions.
if (CurrentSystem == null || !CurrentSystem.ContainsKey("outputValues"))
{
MessageBox.Show("You need to define the Truth Table before you can generate the SOP expressions", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Generate the SOPs.
var sops = GenerateSOPs();
// Preppend the input name to the SOPs.
var prettySops = new List<string>();
int i = 0;
foreach (string s in sops)
prettySops.Add("" + CurrentSystem["outputs"][i++] + ": " + s);
// Open the SOP form
SOPForm sop = new SOPForm();
sop.SOPs = prettySops;
sop.ShowDialog();
}
private void weblabVHDL_Click(object sender, EventArgs e)
{
// Ensure that our system is fully defined. Otherwise we
// can't generate SOP expressions or code.
if (CurrentSystem == null || !CurrentSystem.ContainsKey("outputValues"))
{
MessageBox.Show("You need to define the Truth Table before you can generate the code", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var sops = GenerateSOPs("vhdl");
// Build the code for the equations.
var eqs_array = new List<string>();
int i = 0;
foreach(string s in sops)
{
var sb = new StringBuilder();
sb.Append(" ");
sb.Append(CurrentSystem["outputs"][i++]);
sb.Append(" <= ");
sb.Append(s);
sb.Append(";");
eqs_array.Add(sb.ToString());
}
// Merge every equation into a single string.
string equations = string.Join("\r\n", eqs_array);
// Load the template.
string template = File.ReadAllText("..\\..\\templates\\weblab_combinational.ftemplate");
if(template == null || template.Length == 0)
{
MessageBox.Show("Could not find weblab_combinational.ftemplate Template file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Replace the equations.
FastTemplate ft = new FastTemplate();
string code = ft.Replace(template, new Dictionary<string,string>() { {"equations", equations} });
// Show the save-file dialog.
SaveFileDialog sf = new SaveFileDialog();
sf.AddExtension = true;
sf.Filter = "VHDL file | *.vhd";
sf.ShowDialog();
string file = sf.FileName;
if(file != null && file.Length > 0)
{
// Save the file
File.WriteAllText(file, code);
}
}
private void openWeblab_Click(object sender, EventArgs e)
{
// Ensure that our system is fully defined. Otherwise we
// can't generate SOP expressions or code.
if (CurrentSystem == null || !CurrentSystem.ContainsKey("outputValues"))
{
MessageBox.Show("You need to define the Truth Table before you can generate the code", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Simulate a click so that we force a code generation.
// TODO: Eventually we should only generate code if it is not generated already.
weblabVHDL_Click(sender, e);
// Open Weblab
Process.Start("https://www.weblab.deusto.es/weblab/client/#page=experiment&exp.category=FPGA%20experiments&exp.name=fpga-free");
}
}
}