Merge remote-tracking branch 'origin/dev' into chisel-3.5-published

This commit is contained in:
David Biancolin 2022-02-08 00:23:30 +00:00
commit 5b62907400
109 changed files with 1540 additions and 748 deletions

View File

@ -1 +0,0 @@
*.pyc

View File

@ -1,17 +0,0 @@
#!/usr/bin/env python
from fabric.api import *
from common import manager_fsim_dir, manager_hostname
from ci_variables import ci_workflow_id
def build_default_workloads():
""" Builds workloads that will be run on F1 instances as part of CI """
with prefix('cd {} && source ./env.sh'.format(manager_fsim_dir)), \
prefix('cd deploy/workloads'):
run("marshal -v build br-base.json")
run("make linux-poweroff")
if __name__ == "__main__":
execute(build_default_workloads, hosts=[manager_hostname(ci_workflow_id)])

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python
# Changes the state of instances associated with the CI run's unique tag.
# Where a run is a workflow in CircleCI. Can be used to start, stop, or
# terminate. This may run from either the manager or from the CI instance.
import sys
import argparse
import common
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('tag_value',
help = "The tag used to identify workflow instances.")
parser.add_argument('state_change',
choices = ['terminate', 'stop', 'start'],
help = "The state transition to initiate on workflow instances.")
args = parser.parse_args()
common.change_workflow_instance_states(args.tag_value, args.state_change)

View File

@ -1,265 +0,0 @@
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
orbs:
aws-cli: circleci/aws-cli@1.0.0
executors:
main-env:
docker:
- image: firesim/firesim-ci:v1.1
user: "centos"
environment:
JVM_MEMORY: 3500M # Default JVM maximum heap limit
LANG: en_US.UTF-8 # required by sbt when it sees boost directories
MARSHAL_JLEVEL: 8 # This is unset by default, leading to starvation conditions on the manager
aws:
docker:
- image: cimg/python:2.7-node
commands:
machinelaunchscript:
description: "Run firesim's machine launch script"
steps:
- run:
command: |
cd scripts && /usr/bin/bash ./machine-launch-script.sh
install-ci-python-reqs:
description: "Installs python deps for manager-managing CI container"
steps:
- run:
command: |
pip install -r .circleci/requirements.txt
buildsetup:
description: "Run firesim's build-setup.sh"
steps:
- run:
command: |
./build-setup.sh fast
scala-build:
description: "Compile all relevant Scala sources for CI"
steps:
- run:
command: |
source env.sh
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND=test:compile
build-scala-doc:
description: "Compiles Scala Doc"
steps:
- run:
command: |
source env.sh
make -C sim scaladoc
push-scaladoc-to-ghpages:
description: "Pushes scaladoc to ghphage branch"
steps:
# The default SSH key does not have write permissions
- add_ssh_keys:
fingerprint:
- 0e:d9:c3:3b:62:03:7a:da:17:1f:a9:5a:4f:34:50:4c
- run:
command: |
git config --global user.email "biancolin@berkeley.edu"
git config --global user.name "circleci"
- when:
condition: << pipeline.git.tag >>
steps:
- run:
command: |
source env.sh
export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for << pipeline.git.tag >> release"
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"<< pipeline.git.tag >>\"; ghpagesPushSite'
- unless:
condition: << pipeline.git.tag >>
steps:
- run:
command: |
source env.sh
export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:<< pipeline.git.revision >>"
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND="ghpagesPushSite"
run-scala-test:
description: "Runs the scala test with name <test-package>.<test-name>"
parameters:
target-project:
type: string
default: "midasexamples"
test-name:
type: string
test-package:
type: string
default: "firesim.midasexamples"
timeout:
type: string
default: "120m"
steps:
- run:
command: |
.circleci/run-scala-test.py << parameters.target-project >> << parameters.test-package >>.<< parameters.test-name >>
no_output_timeout: << parameters.timeout >>
initialize-manager:
description: "Setups the manager instance and firesim repo"
parameters:
max-runtime-hours:
type: integer
timeout:
type: string
default: "30m"
steps:
- run:
command: |
.circleci/initialize-manager.py << parameters.max-runtime-hours >>
no_output_timeout: << parameters.timeout >>
# This avoids a race that occurs when multiple cold scala compilations are launched at the same time.
initial-scala-compile:
description: "Initial Scala Compilation"
parameters:
timeout:
type: string
default: "30m"
steps:
- run:
command: |
.circleci/run-sbt-command.py firesim test:compile
no_output_timeout: << parameters.timeout >>
repo-setup:
description: "Runs all baseline setup tasks up to scala compilation in the CI container."
steps:
- checkout
- machinelaunchscript
- buildsetup
- scala-build
repo-setup-aws:
description: "Runs all baseline setup to interact with a AWS-hosted manager instance"
steps:
- checkout
- aws-cli/setup
- install-ci-python-reqs
jobs:
setup-default-manager:
executor: aws
environment:
TERM: xterm-256color
steps:
- repo-setup-aws
- run:
name: Initialize FireSim manager
command: |
.circleci/launch-manager-instance.py
- initialize-manager:
max-runtime-hours: 10
- initial-scala-compile
build-default-workloads:
description: "Invokes marshal to build the default rootfs."
executor: aws
steps:
- repo-setup-aws
- run:
command: |
.circleci/build-default-workloads.py
no_output_timeout: 30m
run-manager-pytests:
executor: aws
steps:
- repo-setup-aws
- run:
command: .circleci/run-manager-pytests.py
run-test-groupA:
executor: aws
steps:
- repo-setup-aws
- run-scala-test:
test-name: "CIGroupA"
run-test-groupB:
executor: aws
steps:
- repo-setup-aws
- run-scala-test:
test-name: "CIGroupB"
run-chipyard-tests:
executor: aws
steps:
- repo-setup-aws
- run-scala-test:
target-project: "firesim"
test-package: "firesim.firesim"
test-name: "CITests"
publish-scala-doc:
executor: main-env
steps:
- repo-setup
- build-scala-doc
- push-scaladoc-to-ghpages
cull-old-ci-instances:
executor: aws
steps:
- repo-setup-aws
- run:
name: Cull Old CI AWS Instances
command: |
.circleci/cull-old-ci-instances.py
workflows:
version: 2
firesimCIall:
jobs:
- setup-default-manager
- build-default-workloads:
requires:
- setup-default-manager
- run-test-groupA:
requires:
- setup-default-manager
- run-test-groupB:
requires:
- run-test-groupA
- run-chipyard-tests:
requires:
- run-test-groupB
- run-manager-pytests:
requires:
- setup-default-manager
# This uses a CI container, not a manager instance
- publish-scala-doc:
filters:
branches:
only:
- dev
tags:
only:
- /[0-9]*\.[0-9]*\.[0-9]*/
cull-old-ci-instances:
triggers:
- schedule:
cron: "0 0,12 * * *"
filters:
branches:
only:
- dev
jobs:
- cull-old-ci-instances

View File

@ -1,4 +0,0 @@
fabric==1.14.0
boto3==1.6.2
pytz
pyyaml

View File

@ -1,15 +0,0 @@
#!/usr/bin/env python
from fabric.api import *
from common import manager_fsim_dir, manager_hostname
from ci_variables import ci_workflow_id
def run_manager_pytests():
""" Runs all manager pytests """
with cd(manager_fsim_dir), prefix('source env.sh'):
run("cd deploy && python -m pytest")
if __name__ == "__main__":
execute(run_manager_pytests, hosts=[manager_hostname(ci_workflow_id)])

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python
import sys
from fabric.api import *
from common import manager_fsim_dir, manager_hostname
from ci_variables import ci_workflow_id
def run_scala_test(target_project, test_name):
""" Runs a scala test under the desired target project
target_project -- The make variable to select the desired target project makefrag
test_name -- the full classname of the test
"""
with cd(manager_fsim_dir), prefix('source env.sh'):
run("make -C sim testOnly TARGET_PROJECT={} SCALA_TEST={}".format(target_project, test_name))
if __name__ == "__main__":
execute(run_scala_test, sys.argv[1], sys.argv[2], hosts = [manager_hostname(ci_workflow_id)])

View File

@ -1,72 +0,0 @@
#!/usr/bin/env python
# Runs in the background on a manager instance to determine when it can be torn
# down by polling the workflow's state using CircleCI's v2.0 restful api.
#
# Terminate instances if:
# - the workflow is successful (all jobs complete successfully)
# - the workflow is cancelled
# Stop instances if:
# - the workflow has failed (all jobs have completed, but at least one has failed)
#
# Other states to consider: not_run, on_hold, unauthorized
import httplib
import time
import sys
import base64
import json
from common import terminate_workflow_instances, stop_workflow_instances
# Time between HTTPS requests to circleci
POLLING_INTERVAL_SECONDS = 60
# Number of failed requests before stopping the instances
QUERY_FAILURE_THRESHOLD = 10
# We should never get to 'not_run' or 'unauthorized' but terminate for good measure
TERMINATE_STATES = ['success', 'canceled', 'not_run', 'unauthorized']
STOP_STATES = ['failed', 'error']
NOP_STATES = ['running', 'failing']
def main(workflow_id, circle_ci_token):
state = None
consecutive_failures = 0
auth_token = base64.b64encode(b"{}:", circle_ci_token)
headers = {'authorization': "Basic {}".format(auth_token)}
while True:
time.sleep(POLLING_INTERVAL_SECONDS)
conn = httplib.HTTPSConnection("circleci.com")
conn.request("GET", "/api/v2/workflow/{}".format(workflow_id), headers=headers)
res = conn.getresponse()
if res.status == httplib.OK:
consecutive_failures = 0
res_dict = json.load(res)
state = res_dict["status"]
print "Workflow {} status: {}".format(workflow_id, state)
if state in TERMINATE_STATES:
terminate_workflow_instances(workflow_id)
exit(0)
elif state in STOP_STATES:
stop_workflow_instances(workflow_id)
exit(0)
elif state not in NOP_STATES:
print "Unexpected Workflow State: {}".format(state)
raise ValueError
else:
print "HTTP GET error: {} {}. Retrying.".format(res.status, res.reason)
consecutive_failures = consecutive_failures + 1
if consecutive_failures == QUERY_FAILURE_THRESHOLD:
stop_workflow_instances(workflow_id)
exit(1)
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])

46
.github/CI_README.md vendored Normal file
View File

@ -0,0 +1,46 @@
FireSim Continuous Integration
==============================
Helpful Links:
* Workflow GUI - https://github.com/firesim/firesim/actions
* Chipyard Explanation of Github Actions (GH-A) - https://github.com/ucb-bar/chipyard/blob/dev/.github/CI_README.md
Github Actions (GH-A) Description
---------------------------------
Much of the following CI infrastructure is based on the Chipyard CI.
For a basic explanation of how the GH-A CI works, see https://github.com/ucb-bar/chipyard/blob/dev/.github/CI_README.md.
However, there are a couple of notable differences/comments as follows:
* In order to provide a fresh environment to test changes, the CI dynamically spawns a AWS instance and sets up GH-A
to use this instance as a GH-A self-hosted runner (see https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners).
* All scripts that run on the manager instance use Fabric with `localhost` and each GH-A step must include `runs-on: ${{ github.run_id }}` KV pair to indicate that you want to run directly on a runner that is on the manager instance.
* Currently only 4 runners are spawned per workflow (reused across multiple workflow runs - i.e. clicking rerun). Every commit gets its own manager instance and 4 runners that run on the manager instance.
* When the CI terminates/stops an instance, the CI automatically deregisters the runners from GH-A (any runner API calls require a personal token with the `repo` scope).
* The CI structure is as follows:
1. Launch the manager instance + setup the N self-hosted runners.
2. Run the original initialization (add the firesim.pem, run the build setup, etc).
3. Continue with the rest of the tests using the GH-A runners.
Running FPGA-related Tasks
--------------------------
CI now includes the capability to run FPGA-simulations on specific PRs.
However, by default, this requires approval from the `firesim-fpga-approval` team (called a "deployment").
You can gain approval to run FPGA-simulations in two ways.
1. Each member in the `firesim-fpga-approval` team will receive an email asking for approval on a specific PR. From that email, they can approve the request and run the FPGA-simulation tests.
2. From the workflow run GUI (go to https://github.com/firesim/firesim/actions and click a specific workflow run) a `firesim-fpga-approval` team member can approve the deployment (note this button only shows up once the job that needs approval is reached).
Debugging Failures
------------------
When a failure occurs on the manager instance the CI will stop or terminate the instance (terminate if your instance is using the spot market).
Currently, the only way to access any running instance that is created from the CI is to do the following:
1. Request the CI PEM file needed to SSH into the CI instances (ask the FireSim developers)
2. Obtain the public IP address from the "Launch AWS instance used for the FireSim manager (instance info found here)" (which runs the `launch-manager-instance.py` script) step in the `setup-self-hosted-manager` job in the CI.
3. SSH into the instance and do any testing required.
If the instance is stopped, then you must request a AWS IAM user account from the FireSim developers to access the EC2 console and restart the instance.

View File

@ -0,0 +1,10 @@
name: build-scala-doc
description: "Compiles Scala Doc"
runs:
using: "composite"
steps:
- run: |
source env.sh
make -C sim scaladoc
shell: bash

8
.github/actions/buildsetup/action.yml vendored Normal file
View File

@ -0,0 +1,8 @@
name: buildsetup
description: "Run firesim's build-setup.sh"
runs:
using: "composite"
steps:
- run: ./build-setup.sh fast
shell: bash

View File

@ -0,0 +1,8 @@
name: initial-scala-compile
description: "Initial Scala Compilation"
runs:
using: "composite"
steps:
- run: .github/scripts/run-sbt-command.py firesim test:compile
shell: bash

View File

@ -0,0 +1,13 @@
name: initialize-manager
description: "Sets up the manager instance and firesim repo"
inputs:
max-runtime-hours:
description: "Max runtime hours"
required: true
runs:
using: "composite"
steps:
- run: .github/scripts/initialize-manager.py ${{ inputs.max-runtime-hours }}
shell: bash

View File

@ -0,0 +1,10 @@
name: install-ci-python-reqs
description: "Installs python deps for manager-managing CI container"
runs:
using: "composite"
steps:
- run: |
pip3 install --upgrade pip==21.3.1
python3 -m pip install -r .github/scripts/requirements.txt
shell: bash

8
.github/actions/job-end/action.yml vendored Normal file
View File

@ -0,0 +1,8 @@
name: job-end
description: "Save a job status"
runs:
using: "composite"
steps:
- run: echo "success" > run_result
shell: bash

19
.github/actions/job-start/action.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: job-start
description: "Setup a job status"
outputs:
run_result:
value: ${{ steps.run_result.outputs.run_result }}
runs:
using: "composite"
steps:
- name: Restore the previous run result
uses: actions/cache@v2
with:
path: run_result
key: ${{ github.run_id }}-${{ github.job }}
restore-keys: ${{ github.run_id }}-${{ github.job }}
- name: Set run_result to default or use cached value
id: run_result
run: echo "::set-output name=run_result::$(cat run_result 2>/dev/null || echo 'default')"
shell: bash

View File

@ -0,0 +1,10 @@
name: machinelaunchscript
description: "Run FireSim's machine-launch-script.sh"
runs:
using: "composite"
steps:
- run: |
sudo yum -y remove git git224 git224-core ius-release.noarch # remove any older git versions and collateral first
cd scripts/ && /usr/bin/bash machine-launch-script.sh
shell: bash

View File

@ -0,0 +1,27 @@
name: push-scaladoc-to-ghpages
description: "Pushes scaladoc to ghphage branch"
runs:
using: "composite"
steps:
- name: Install SSH key for Github.com
uses: webfactory/ssh-agent@v0.5.4
with:
ssh-private-key: ${{ env.FIRESIM-REPO-DEP-KEY }}
- run: |
git config --global user.email "abe.gonzalez@berkeley.edu"
git config --global user.name "github-actions"
shell: bash
- run: |
if [[ "${{ github.ref_type }}" != 'tag' ]]; then
source env.sh
export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for ${{ github.ref_type }} release"
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"${{ github.ref_type }}\"; ghpagesPushSite'
else
source env.sh
export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:${{ github.sha }}"
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND="ghpagesPushSite"
fi
shell: bash

View File

@ -0,0 +1,17 @@
name: repo-setup-aws
description: "Runs all baseline setup to interact with a AWS-hosted manager instance"
runs:
using: "composite"
steps:
- uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ env.AWS-ACCESS-KEY-ID }}
aws-secret-access-key: ${{ env.AWS-SECRET-ACCESS-KEY }}
aws-region: ${{ env.AWS-DEFAULT-REGION }}
- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ env.FIRESIM_PEM }}
known_hosts: unnecessary
- uses: ./.github/actions/install-ci-python-reqs

9
.github/actions/repo-setup/action.yml vendored Normal file
View File

@ -0,0 +1,9 @@
name: repo-setup
description: "Runs all baseline setup tasks up to scala compilation in the CI container."
runs:
using: "composite"
steps:
- uses: ./.github/actions/machinelaunchscript
- uses: ./.github/actions/buildsetup
- uses: ./.github/actions/scala-build

View File

@ -0,0 +1,22 @@
name: run-scala-test
description: "Runs the scala test with name <test-package>.<test-name>"
inputs:
target-project:
description: "Target project"
required: false
default: "midasexamples"
test-name:
description: "Test name"
required: true
test-package:
description: "Test package"
required: false
default: "firesim.midasexamples"
runs:
using: "composite"
steps:
- run: |
.github/scripts/run-scala-test.py ${{ inputs.target-project }} ${{ inputs.test-package }}.${{ inputs.test-name }}
shell: bash

10
.github/actions/scala-build/action.yml vendored Normal file
View File

@ -0,0 +1,10 @@
name: scala-build
description: "Compile all relevant Scala sources for CI"
runs:
using: "composite"
steps:
- run: |
source env.sh
make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND=test:compile
shell: bash

View File

@ -6,13 +6,14 @@ RUN yum -y install sudo epel-release
RUN yum -y install python-pip
# Match the version on the dev ami
RUN pip2 install --upgrade pip==18.0
# Provide a baseline of version for circle CI to use.
# If Circle CI uses its native version to initialize the repo, future submodule
# Provide a baseline of version for GH-A CI to use.
# If GH-A CI uses its native version to initialize the repo, future submodule
# initializations with the machine-launch installed version of git produce very
# non-intuitive results
# (the chipyard submodule is initialized to an apparently random commit)
# If we want to get rid of this we could reclone the repo under the updated git
RUN yum -y install git
RUN yum -y install https://repo.ius.io/ius-release-el7.rpm
RUN yum -y install git224
RUN adduser centos
RUN usermod -aG wheel centos

25
.github/scripts/build-default-workloads.py vendored Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
from fabric.api import *
from common import manager_fsim_dir, set_fabric_firesim_pem
def build_default_workloads():
""" Builds workloads that will be run on F1 instances as part of CI """
with prefix('cd {} && source ./env.sh'.format(manager_fsim_dir)), \
prefix('cd deploy/workloads'):
# avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output)
with settings(warn_only=True):
rc = run("marshal -v build br-base.json &> br-base.full.log").return_code
if rc != 0:
run("cat br-base.full.log")
raise Exception("Building br-base.json failed to run")
run("make linux-poweroff")
run("make allpaper")
if __name__ == "__main__":
set_fabric_firesim_pem()
execute(build_default_workloads, hosts=["localhost"])

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# Changes the state of instances associated with the CI workflow run's unique tag.
# Can be used to start, stop, or terminate. This may run from either the manager
# or from the CI instance.
import argparse
from common import change_workflow_instance_states
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('tag_value',
help = "The tag used to identify workflow instances.")
parser.add_argument('state_change',
choices = ['terminate', 'stop', 'start'],
help = "The state transition to initiate on workflow instances.")
parser.add_argument('github_api_token',
help = "API token to modify self-hosted runner state.")
args = parser.parse_args()
change_workflow_instance_states(args.github_api_token, args.tag_value, args.state_change)

View File

@ -5,9 +5,9 @@ import os
# CI instance environment variables
# This is used as a unique tag for all instances launched in a workflow
ci_workflow_id = os.environ['CIRCLE_WORKFLOW_ID']
ci_commit_sha1 = os.environ['CIRCLE_SHA1']
ci_workflow_run_id = os.environ['GITHUB_RUN_ID']
ci_commit_sha1 = os.environ['GITHUB_SHA']
# expanduser to replace the ~ present in the default, for portability
ci_workdir = os.path.expanduser(os.environ['CIRCLE_WORKING_DIRECTORY'])
ci_api_token = os.environ['CIRCLE_CI_API_TOKEN']
ci_workdir = os.path.expanduser(os.environ['GITHUB_WORKSPACE'])
ci_api_token = os.environ['GITHUB_TOKEN']
ci_personal_api_token = os.environ['PERSONAL_ACCESS_TOKEN']

View File

@ -1,21 +1,31 @@
import sys
import boto3
import os
from fabric.api import *
import requests
# Reuse manager utilities
from ci_variables import ci_workdir
sys.path.append(ci_workdir + "/deploy/awstools")
script_dir = os.path.dirname(os.path.realpath(__file__)) + "/../.."
sys.path.append(script_dir + "/deploy/awstools")
from awstools import get_instances_with_filter
# Remote paths
manager_home_dir = "/home/centos"
manager_fsim_dir = "/home/centos/firesim"
manager_fsim_pem = manager_home_dir + "/firesim.pem"
manager_fsim_dir = manager_home_dir + "/firesim"
manager_marshal_dir = manager_fsim_dir + "/target-design/chipyard/software/firemarshal"
manager_ci_dir = manager_fsim_dir + "/.circleci"
manager_ci_dir = manager_fsim_dir + "/.github/scripts"
# Common fabric settings
env.output_prefix = False
env.abort_on_prompts = True
env.timeout = 100
env.connection_attempts = 10
env.disable_known_hosts = True
env.keepalive = 60 # keep long SSH connections running
def set_fabric_firesim_pem():
env.key_filename = manager_fsim_pem
# This tag is common to all instances launched as part of a given workflow
unique_tag_key = 'ci-workflow-id'
@ -35,7 +45,7 @@ def get_manager_tag_dict(sha, tag_value):
unique_tag_key: tag_value}
def get_manager_instance(tag_value):
""" Looks up the manager instance dict using the CI run's unique tag"""
""" Looks up the manager instance dict using the CI workflow run's unique tag"""
instances = get_instances_with_filter([get_ci_filter(tag_value), manager_filter])
if instances:
assert len(instances) == 1
@ -44,7 +54,7 @@ def get_manager_instance(tag_value):
return None
def get_manager_instance_id(tag_value):
""" Looks up the manager instance ID using the CI run's unique tag"""
""" Looks up the manager instance ID using the CI workflow run's unique tag"""
manager = get_manager_instance(tag_value)
if manager is None:
@ -54,7 +64,7 @@ def get_manager_instance_id(tag_value):
return manager['InstanceId']
def get_manager_ip(tag_value):
""" Looks up the manager IP using the CI run's unique tag"""
""" Looks up the manager IP using the CI workflow run's unique tag"""
manager = get_manager_instance(tag_value)
if manager is None:
@ -67,7 +77,7 @@ def manager_hostname(tag_value):
return "centos@{}".format(get_manager_ip(tag_value))
def get_all_workflow_instances(tag_value):
""" Grabs a list of all instance dicts sharing the CI run's unique tag """
""" Grabs a list of all instance dicts sharing the CI workflow run's unique tag """
return get_instances_with_filter([get_ci_filter(tag_value)])
def instance_metadata_str(instance):
@ -83,9 +93,26 @@ def instance_metadata_str(instance):
return static_md + dynamic_md
def deregister_runner_if_exists(gh_token, runner_name):
headers = {'Authorization': "token {}".format(gh_token.strip())}
def change_workflow_instance_states(tag_value, state_change, dryrun=False):
""" Change the state of all instances sharing the same CI run's tag. """
# Check if exists before deregistering
r = requests.get("https://api.github.com/repos/firesim/firesim/actions/runners", headers=headers)
if r.status_code != 200:
# if couldn't delete then just exit
return
res_dict = r.json()
runner_list = res_dict["runners"]
for runner in runner_list:
if runner_name in runner["name"]:
r = requests.delete("https://api.github.com/repos/firesim/firesim/actions/runners/{}".format(runner["id"]), headers=headers)
if r.status_code != 204:
# if couldn't delete then just exit
return
def change_workflow_instance_states(gh_token, tag_value, state_change, dryrun=False):
""" Change the state of all instances sharing the same CI workflow run's tag. """
all_instances = get_all_workflow_instances(tag_value)
manager_instance = get_manager_instance(tag_value)
@ -100,6 +127,7 @@ def change_workflow_instance_states(tag_value, state_change, dryrun=False):
client = boto3.client('ec2')
if state_change == 'stop':
print("Stopping instances: {}".format(", ".join(instance_ids)))
deregister_runner_if_exists(gh_token, tag_value)
client.stop_instances(InstanceIds=instance_ids, DryRun=dryrun)
elif state_change == 'start':
print("Starting instances: {}".format(", ".join(instance_ids)))
@ -116,15 +144,16 @@ def change_workflow_instance_states(tag_value, state_change, dryrun=False):
elif state_change == 'terminate':
print("Terminating instances: {}".format(", ".join(instance_ids)))
deregister_runner_if_exists(gh_token, tag_value)
client.terminate_instances(InstanceIds=instance_ids, DryRun=dryrun)
else:
raise ValueError("Unrecognized transition type: {}".format(state_change))
def terminate_workflow_instances(tag_value, dryrun=False):
change_workflow_instance_states(tag_value, "terminate", dryrun)
def terminate_workflow_instances(gh_token, tag_value, dryrun=False):
change_workflow_instance_states(gh_token, tag_value, "terminate", dryrun)
def stop_workflow_instances(tag_value, dryrun=False):
change_workflow_instance_states(tag_value, "stop", dryrun)
def stop_workflow_instances(gh_token, tag_value, dryrun=False):
change_workflow_instance_states(gh_token, tag_value, "stop", dryrun)
def start_workflow_instances(tag_value, dryrun=False):
change_workflow_instance_states(tag_value, "start", dryrun)
def start_workflow_instances(gh_token, tag_value, dryrun=False):
change_workflow_instance_states(gh_token, tag_value, "start", dryrun)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Runs periodically in it's own workflow in the CI/CD environment to teardown
# instances that have exceeded a lifetime limit
@ -8,10 +8,10 @@ import pytz
import boto3
import sys
from common import unique_tag_key
from common import unique_tag_key, deregister_runner_if_exists
# Reuse manager utilities
from ci_variables import ci_workdir
from ci_variables import ci_workdir, ci_personal_api_token, ci_workflow_run_id
sys.path.append(ci_workdir + "/deploy/awstools")
from awstools import get_instances_with_filter
@ -27,12 +27,13 @@ def main():
all_ci_instances = get_instances_with_filter([all_ci_instances_filter], allowed_states=['*'])
client = boto3.client('ec2')
print "Terminated Instances:"
print("Terminated Instances:")
for inst in all_ci_instances:
lifetime_secs = (current_time - inst["LaunchTime"]).total_seconds()
if lifetime_secs > (INSTANCE_LIFETIME_LIMIT_HOURS * 3600):
deregister_runner_if_exists(ci_personal_api_token, ci_workflow_run_id):
client.terminate_instances(InstanceIds=[inst["InstanceId"]])
print " " + inst["InstanceId"]
print(" " + inst["InstanceId"])
if __name__ == "__main__":
main()

16
.github/scripts/gh-a-runner.expect vendored Executable file
View File

@ -0,0 +1,16 @@
#! /usr/bin/env expect
set timeout -1
set token [lindex $argv 0]
set runner_name [lindex $argv 1]
set unique_label [lindex $argv 2]
spawn ./config.sh --url https://github.com/firesim/firesim --token $token
send -- "\r"
send -- "$runner_name\r"
send -- "$unique_label\r"
send -- "\r"
expect eof

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import traceback
import time
from fabric.api import *
@ -12,20 +13,24 @@ def initialize_manager(max_runtime):
""" Performs the prerequisite tasks for all CI jobs that will run on the manager instance
max_runtime (hours): The maximum uptime this manager and its associated
instances should have before it is stopped. This serves as a redundant check
instances should have before it is stopped. This serves as a redundant check
in case the workflow-monitor is brought down for some reason.
"""
# Catch any exception that occurs so that we can gracefully teardown
try:
put(ci_workdir + "/scripts/machine-launch-script.sh", manager_home_dir)
# wait until machine launch is complete
with cd(manager_home_dir):
run("chmod +x machine-launch-script.sh")
run("sudo ./machine-launch-script.sh")
run("git clone https://github.com/firesim/firesim.git")
# add firesim.pem
with open(manager_fsim_pem, "w") as pem_file:
pem_file.write(os.environ["FIRESIM_PEM"])
local("chmod 600 {}".format(manager_fsim_pem))
set_fabric_firesim_pem()
# copy ci version of the repo into the new globally accessible location
run("git clone {} {}".format(ci_workdir, manager_fsim_dir))
with cd(manager_fsim_dir):
run("git checkout " + ci_commit_sha1)
run("./build-setup.sh --fast")
# Initialize marshal submodules early because it appears some form of
@ -36,27 +41,27 @@ def initialize_manager(max_runtime):
run("./init-submodules.sh")
with cd(manager_fsim_dir), prefix("source ./sourceme-f1-manager.sh"):
run(".circleci/firesim-managerinit.expect {} {} {}".format(
os.environ["AWS_ACCESS_KEY_ID"],
os.environ["AWS_SECRET_ACCESS_KEY"],
os.environ["AWS_DEFAULT_REGION"]))
run(".github/scripts/firesim-managerinit.expect {} {} {}".format(
os.environ["AWS-ACCESS-KEY-ID"],
os.environ["AWS-SECRET-ACCESS-KEY"],
os.environ["AWS-DEFAULT-REGION"]))
with cd(manager_ci_dir):
# Put a baseline time-to-live bound on the manager.
# Instances will be stopped and cleaned up in a nightly job.
# Instances will be terminated (since they are spot requests) and cleaned up in a nightly job.
# Setting pty=False is required to stop the screen from being
# culled when the SSH session associated with teh run command ends.
run("screen -S ttl -dm bash -c \'sleep {}; ./change-workflow-instance-states.py {} stop\'"
.format(int(max_runtime) * 3600, ci_workflow_id), pty=False)
# culled when the SSH session associated with the run command ends.
run("screen -S ttl -dm bash -c \'sleep {}; ./change-workflow-instance-states.py {} terminate {}\'"
.format(int(max_runtime) * 3600, ci_workflow_run_id, ci_personal_api_token), pty=False)
run("screen -S workflow-monitor -L -dm ./workflow-monitor.py {} {}"
.format(ci_workflow_id, ci_api_token), pty=False)
.format(ci_workflow_run_id, ci_personal_api_token), pty=False)
except BaseException as e:
traceback.print_exc(file=sys.stdout)
terminate_workflow_instances(ci_workflow_id)
terminate_workflow_instances(ci_personal_api_token, ci_workflow_run_id)
sys.exit(1)
if __name__ == "__main__":
max_runtime = sys.argv[1]
execute(initialize_manager, max_runtime, hosts=[manager_hostname(ci_workflow_id)])
execute(initialize_manager, max_runtime, hosts=["localhost"])

View File

@ -1,11 +1,9 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Used to launch a fresh manager instance from the CI container.
import sys
from fabric.api import *
# This must run in the CI container
from ci_variables import *
from common import *
@ -16,7 +14,7 @@ import awstools
def main():
""" Spins up a new manager instance for our CI run """
manager_instance = get_manager_instance(ci_workflow_id)
manager_instance = get_manager_instance(ci_workflow_run_id)
if manager_instance is not None:
print("There is an existing manager instance for this CI workflow:")
print(instance_metadata_str(manager_instance))
@ -27,11 +25,13 @@ def main():
'launch',
'--inst_type', 'z1d.2xlarge',
'--block_devices', str([{'DeviceName':'/dev/sda1','Ebs':{'VolumeSize':300,'VolumeType':'gp2'}}]),
'--tags', str(get_manager_tag_dict(ci_commit_sha1, ci_workflow_id))])
manager_instance = get_manager_instance(ci_workflow_id)
'--tags', str(get_manager_tag_dict(ci_commit_sha1, ci_workflow_run_id)),
'--user_data_file', ci_workdir + "/scripts/machine-launch-script.sh"
])
manager_instance = get_manager_instance(ci_workflow_run_id)
print("Instance ready.")
print(instance_metadata_str(get_manager_instance(ci_workflow_id)))
print(instance_metadata_str(get_manager_instance(ci_workflow_run_id)))
sys.stdout.flush()
if __name__ == "__main__":

5
.github/scripts/requirements.txt vendored Normal file
View File

@ -0,0 +1,5 @@
fab-classic==1.19.1
boto3==1.20.21
pytz
pyyaml
requests

88
.github/scripts/run-ini-api-tests.py vendored Executable file
View File

@ -0,0 +1,88 @@
#!/usr/bin/env python3
from fabric.api import *
from common import manager_fsim_dir, manager_ci_dir, manager_fsim_pem, set_fabric_firesim_pem
import sys
def run_build_recipes_ini_api_tests():
""" Test config_{build, build_recipes}.ini APIs """
def commands_to_run(commands, opts):
""" Run a list of commands with the specified opts """
for command in commands:
with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)):
rc = 0
with settings(warn_only=True):
rc = run("{} {}".format(command, opts)).return_code
if rc == 0:
print("{} passed unexpectedly.".format(command))
# exit since passing is not wanted
sys.exit(1)
def run_test(name):
# test files should exist on the manager already
test_dir = "{}/ini-tests/failing-buildafi-files/{}".format(manager_ci_dir, name)
commands_to_run(
["firesim buildafi"],
"-b {}/sample_config_build.ini -r {}/sample_config_build_recipes.ini".format(test_dir, test_dir))
run_test("invalid-build-section")
run_test("invalid-recipe-inst-type")
# test invalid config_build.ini
commands_to_run(["firesim buildafi"], "-b ~/GHOST_FILE")
# test invalid config_build_recipes.ini
commands_to_run(["firesim buildafi"], "-r ~/GHOST_FILE")
def run_runtime_hwdb_ini_api_tests():
""" Test config_{runtime, hwdb}.ini APIs """
def commands_to_run(commands, opts):
""" Run a list of commands with the specified opts """
for command in commands:
with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)):
rc = 0
with settings(warn_only=True):
rc = run("{} {}".format(command, opts)).return_code
if rc == 0:
print("{} passed unexpectedly.".format(command))
# test passed so make sure to terminate runfarm
run("firesim terminaterunfarm -q {}".format(opts))
# exit since passing is not wanted
sys.exit(1)
def run_test(name):
# test files should exist on the manager already
test_dir = "{}/ini-tests/failing-runtime-files/{}".format(manager_ci_dir, name)
commands_to_run(
["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"],
"-c {}/sample_config_runtime.ini -a {}/sample_config_hwdb.ini".format(test_dir, test_dir))
run_test("hwdb-invalid-afi")
run_test("runtime-invalid-hwconfig")
run_test("runtime-invalid-topology")
run_test("runtime-invalid-workloadname")
# test invalid config_runtime.ini
commands_to_run(["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"], "-c ~/GHOST_FILE")
# test invalid config_hwdb.ini
commands_to_run(["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"], "-a ~/GHOST_FILE")
def run_ini_api_tests():
""" Test manager .ini file APIs """
run_build_recipes_ini_api_tests()
run_runtime_hwdb_ini_api_tests()
if __name__ == "__main__":
set_fabric_firesim_pem()
execute(run_ini_api_tests, hosts=["localhost"])

44
.github/scripts/run-linux-poweroff.py vendored Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
import sys
from fabric.api import *
from common import manager_fsim_dir, set_fabric_firesim_pem
def run_linux_poweroff():
""" Runs Linux poweroff workloads """
with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)):
run("cd sw/firesim-software && ./marshal -v build br-base.json && ./marshal -v install br-base.json")
run("cd deploy/workloads/ && make linux-poweroff")
def run_w_timeout(workload, timeout):
""" Run workload with a specific timeout
:arg: workload (str) - workload ini (abs path)
:arg: timeout (str) - timeout amount for the workload to run
"""
rc = 0
with settings(warn_only=True):
# avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output)
# pty=False needed to avoid issues with screen -ls stalling in fabric
rc = run("timeout {} ./deploy/workloads/run-workload.sh {} --withlaunch &> {}.log".format(timeout, workload, workload), pty=False).return_code
if rc != 0:
# need to confirm that instance is off
print("Workload {} failed. Printing last lines of log. See {}.log for full info".format(workload, workload))
print("Log start:")
run("tail -n 100 {}.log".format(workload))
print("Log end.")
print("Terminating workload")
run("firesim terminaterunfarm -q -c {}".format(workload))
sys.exit(rc)
else:
print("Workload {} successful.".format(workload))
run_w_timeout("{}/deploy/workloads/linux-poweroff-all-no-nic.ini".format(manager_fsim_dir), "30m")
run_w_timeout("{}/deploy/workloads/linux-poweroff-nic.ini".format(manager_fsim_dir), "30m")
if __name__ == "__main__":
set_fabric_firesim_pem()
execute(run_linux_poweroff, hosts=["localhost"])

15
.github/scripts/run-manager-pytests.py vendored Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
from fabric.api import *
from common import manager_fsim_dir, set_fabric_firesim_pem
def run_manager_pytests():
""" Runs all manager pytests """
with cd(manager_fsim_dir), prefix('source env.sh'):
run("cd deploy && python3 -m pytest")
if __name__ == "__main__":
set_fabric_firesim_pem()
execute(run_manager_pytests, hosts=["localhost"])

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import sys
from fabric.api import *
from common import manager_fsim_dir, manager_hostname
from ci_variables import ci_workflow_id
from common import manager_fsim_dir, manager_hostname, set_fabric_firesim_pem
from ci_variables import ci_workflow_run_id
def run_sbt_command(target_project, command):
""" Runs a command in SBT shell for the default project specified by the target_project makefrag
@ -19,4 +19,5 @@ def run_sbt_command(target_project, command):
run("make -C sim sbt SBT_COMMAND={} TARGET_PROJECT={}".format(command, target_project))
if __name__ == "__main__":
execute(run_sbt_command, sys.argv[1], sys.argv[2], hosts=[manager_hostname(ci_workflow_id)])
set_fabric_firesim_pem()
execute(run_sbt_command, sys.argv[1], sys.argv[2], hosts=["localhost"])

26
.github/scripts/run-scala-test.py vendored Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python3
import sys
from fabric.api import *
from common import manager_fsim_dir, set_fabric_firesim_pem
def run_scala_test(target_project, test_name):
""" Runs a scala test under the desired target project
target_project -- The make variable to select the desired target project makefrag
test_name -- the full classname of the test
"""
with cd(manager_fsim_dir), prefix('source env.sh'):
# avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output)
with settings(warn_only=True):
rc = run("make -C sim testOnly TARGET_PROJECT={} SCALA_TEST={} &> scala-test.full.log".format(target_project, test_name)).return_code
if rc != 0:
run("cat scala-test.full.log")
raise Exception("Running scala test failed")
if __name__ == "__main__":
set_fabric_firesim_pem()
execute(run_scala_test, sys.argv[1], sys.argv[2], hosts = ["localhost"])

82
.github/scripts/setup-manager-self-hosted.py vendored Executable file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
import traceback
import time
import requests
import sys
from fabric.api import *
from common import *
# This is expected to be launch from the ci container
from ci_variables import *
def initialize_manager_hosted():
""" Performs the prerequisite tasks for all CI jobs that will run on the manager instance
max_runtime (hours): The maximum uptime this manager and its associated
instances should have before it is stopped. This serves as a redundant check
in case the workflow-monitor is brought down for some reason.
"""
# Catch any exception that occurs so that we can gracefully teardown
try:
# wait until machine launch is complete
with cd(manager_home_dir):
with settings(warn_only=True):
rc = run("timeout 10m grep -q '.*machine launch script complete.*' <(tail -f machine-launchstatus)").return_code
if rc != 0:
run("cat machine-launchstatus.log")
raise Exception("machine-launch-script.sh failed to run")
# get the runner version based off the latest tag on the github runner repo
RUNNER_VERSION = local("git ls-remote --refs --tags https://github.com/actions/runner.git | cut --delimiter='/' --fields=3 | tr '-' '~' | sort --version-sort | tail --lines=1", capture=True)
RUNNER_VERSION = RUNNER_VERSION.replace("v", "")
print("Using Github Actions Runner v{}".format(RUNNER_VERSION))
# create NUM_RUNNER self-hosted runners on the manager that run in parallel
NUM_RUNNERS = 4
for runner_idx in range(NUM_RUNNERS):
actions_dir = "{}/actions-runner-{}".format(manager_home_dir, runner_idx)
run("mkdir -p {}".format(actions_dir))
with cd(actions_dir):
run("curl -o actions-runner-linux-x64-{}.tar.gz -L https://github.com/actions/runner/releases/download/v{}/actions-runner-linux-x64-{}.tar.gz".format(RUNNER_VERSION, RUNNER_VERSION, RUNNER_VERSION))
run("tar xzf ./actions-runner-linux-x64-{}.tar.gz".format(RUNNER_VERSION))
# install deps
run("sudo ./bin/installdependencies.sh")
# get registration token from API
headers = {'Authorization': "token {}".format(ci_personal_api_token.strip())}
r = requests.post("https://api.github.com/repos/firesim/firesim/actions/runners/registration-token", headers=headers)
if r.status_code != 201:
raise Exception("HTTPS error: {} {}. Retrying.".format(r.status_code, r.json()))
res_dict = r.json()
reg_token = res_dict["token"]
# config runner
put(".github/scripts/gh-a-runner.expect", actions_dir)
run("chmod +x gh-a-runner.expect")
runner_name = "{}-{}".format(ci_workflow_run_id, runner_idx) # used to teardown runner
unique_label = ci_workflow_run_id # used within the yaml to choose a runner
run("./gh-a-runner.expect {} {} {}".format(reg_token, runner_name, unique_label))
# start runner
# Setting pty=False is required to stop the screen from being
# culled when the SSH session associated with the run command ends.
run("screen -S gh-a-runner-{} -L -dm ./run.sh".format(runner_idx), pty=False)
# double check that screen is setup properly
with settings(warn_only=True):
rc = run("screen -ls | grep \"gh-a-runner-{}\"".format(runner_idx)).return_code
if rc != 0:
run("cat screenlog.*")
raise Exception("There was an issue with setting up Github Actions runner {}".format(runner_idx))
except BaseException as e:
traceback.print_exc(file=sys.stdout)
terminate_workflow_instances(ci_personal_api_token, ci_workflow_run_id)
sys.exit(1)
if __name__ == "__main__":
execute(initialize_manager_hosted, hosts=[manager_hostname(ci_workflow_run_id)])

71
.github/scripts/workflow-monitor.py vendored Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
# Runs in the background on a manager instance to determine when it can be torn
# down by polling the workflow's state using GitHub Actions's RESTful api.
#
# Site: https://docs.github.com/en/rest/reference/actions#get-a-workflow-run
#
# Terminate instances if:
# - the workflow is successful (all jobs complete successfully)
# - the workflow is cancelled
# Stop instances if:
# - the workflow has failed (all jobs have completed, but at least one has failed)
#
# Other states to consider: not_run, on_hold, unauthorized
import time
import sys
import requests
from common import terminate_workflow_instances, stop_workflow_instances
# Time between HTTPS requests to github
POLLING_INTERVAL_SECONDS = 60
# Number of failed requests before stopping the instances
QUERY_FAILURE_THRESHOLD = 10
# We should never get to 'not_run' or 'unauthorized' but terminate for good measure
TERMINATE_STATES = ["cancelled", "success", "skipped", "stale"]
STOP_STATES = ["failure", "timed_out"]
NOP_STATES = ["action_required"] # TODO: unsure when this happens
def main(workflow_id, gha_ci_personal_token):
state = None
consecutive_failures = 0
headers = {'Authorization': "token {}".format(gha_ci_personal_token.strip())}
while True:
time.sleep(POLLING_INTERVAL_SECONDS)
res = requests.get("https://api.github.com/repos/firesim/firesim/actions/runs/{}".format(workflow_id), headers=headers)
if res.status_code == 200:
consecutive_failures = 0
res_dict = res.json()
state_status = res_dict["status"]
state_concl = res_dict["conclusion"]
print("Workflow {} status: {} {}".format(workflow_id, state_status, state_concl))
if state_status in ['completed']:
if state_concl in TERMINATE_STATES:
terminate_workflow_instances(gha_ci_personal_token, workflow_id)
exit(0)
elif state_concl in STOP_STATES:
stop_workflow_instances(gha_ci_personal_token, workflow_id)
exit(0)
elif state_concl not in NOP_STATES:
print("Unexpected Workflow State On Completed: {}".format(state_concl))
raise ValueError
elif state_status not in ['in_progress', 'queued', 'waiting', 'requested']:
print("Unexpected Workflow State: {}".format(state_status))
raise ValueError
else:
print("HTTP GET error: {}. Retrying.".format(res.json()))
consecutive_failures = consecutive_failures + 1
if consecutive_failures == QUERY_FAILURE_THRESHOLD:
stop_workflow_instances(gha_ci_personal_token, workflow_id)
exit(1)
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])

View File

@ -0,0 +1,26 @@
name: firesim-cull-instances
on:
schedule:
- cron: "0 0,12 * * *"
env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_A_PERSONAL_ACCESS_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }}
FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }}
jobs:
cull-old-ci-instances:
name: cull-old-ci-instances
runs-on: ubuntu-latest
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
with:
ref: dev
- uses: ./.github/actions/repo-setup-aws
- run: .github/scripts/cull-old-ci-instances.py

View File

@ -0,0 +1,34 @@
name: firesim-publish-scala-doc
on:
push:
branches:
- dev
tags:
- '[0-9]*.[0-9]*.[0-9]*'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }}
FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }}
FIRESIM-REPO-DEP-KEY: ${{ secrets.FIRESIM_REPO_DEP_KEY }}
LANG: "en_US.UTF-8" # required by SBT when it sees boost directories
LANGUAGE: "en_US:en"
LC_ALL: "en_US.UTF-8"
jobs:
publish-scala-doc:
name: publish-scala-doc
runs-on: ubuntu-18.04
container:
image: firesim/firesim-ci:v1.3
options: --entrypoint /bin/bash
env:
JVM_MEMORY: 3500M # Default JVM maximum heap limit
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/repo-setup
- uses: ./.github/actions/build-scala-doc
- uses: ./.github/actions/push-scaladoc-to-ghpages

204
.github/workflows/firesim-run-tests.yml vendored Normal file
View File

@ -0,0 +1,204 @@
name: firesim-ci-process
on:
# run ci when pring to dev/master/main (note: ci runs on the merge commit of the pr!)
pull_request:
branches:
- dev
- master
- main
env:
PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_A_PERSONAL_ACCESS_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }}
FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }}
LANG: "en_US.UTF-8" # required by SBT when it sees boost directories
LANGUAGE: "en_US:en"
LC_ALL: "en_US.UTF-8"
jobs:
cancel-prior-workflows:
name: cancel-prior-workflows
runs-on: ubuntu-18.04
steps:
- name: Cancel previous workflow runs
uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ github.token }}
setup-self-hosted-manager:
name: setup-self-hosted-manager
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Install Python CI requirements
uses: ./.github/actions/repo-setup-aws
if: steps.job-start.outputs.run_result != 'success'
- name: Launch AWS instance used for the FireSim manager (instance info found here)
run: ./.github/scripts/launch-manager-instance.py
if: steps.job-start.outputs.run_result != 'success'
- name: Setup N Github Actions Runners on AWS instance
run: ./.github/scripts/setup-manager-self-hosted.py
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
setup-manager:
name: setup-manager
needs: [setup-self-hosted-manager]
runs-on: ${{ github.run_id }}
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Setup FireSim repo (.pem, build-setup.sh, AWS credentials, submodules) and CI daemons
uses: ./.github/actions/initialize-manager
if: steps.job-start.outputs.run_result != 'success'
with:
max-runtime-hours: 10
- name: Initial Scala compilation
uses: ./.github/actions/initial-scala-compile
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
build-default-workloads:
name: build-default-workloads
needs: [setup-manager]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Build default workloads (FireMarshal and paper workloads)
run: .github/scripts/build-default-workloads.py
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
run-manager-pytests:
name: run-manager-pytests
needs: [setup-manager]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run pytests
run: .github/scripts/run-manager-pytests.py
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
run-test-groupA:
name: run-test-groupA
needs: [setup-manager]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run CIGroupA Scala tests
uses: ./.github/actions/run-scala-test
if: steps.job-start.outputs.run_result != 'success'
with:
test-name: "CIGroupA"
- uses: ./.github/actions/job-end
run-test-groupB:
name: run-test-groupB
needs: [run-test-groupA]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run CIGroupB Scala tests
uses: ./.github/actions/run-scala-test
if: steps.job-start.outputs.run_result != 'success'
with:
test-name: "CIGroupB"
- uses: ./.github/actions/job-end
run-chipyard-tests:
name: run-chipyard-tests
needs: [run-test-groupB]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run other (CITests) Scala tests
uses: ./.github/actions/run-scala-test
if: steps.job-start.outputs.run_result != 'success'
with:
target-project: "firesim"
test-package: "firesim.firesim"
test-name: "CITests"
- uses: ./.github/actions/job-end
run-basic-linux-poweroff:
name: run-basic-linux-poweroff
needs: [build-default-workloads]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
environment: use-fpgas
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run linux-poweroff test
run: .github/scripts/run-linux-poweroff.py
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
run-ini-api-tests:
name: run-ini-api-tests
needs: [setup-manager]
runs-on: ${{ github.run_id }}
env:
TERM: xterm-256-color
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Run .ini API verification tests
run: .github/scripts/run-ini-api-tests.py
if: steps.job-start.outputs.run_result != 'success'
- uses: ./.github/actions/job-end
documentation-check:
name: documentation-check
runs-on: ubuntu-18.04
container:
image: firesim/firesim-ci:v1.3
options: --entrypoint /bin/bash
env:
JVM_MEMORY: 3500M # Default JVM maximum heap limit
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/job-start
id: job-start
- name: Check that documentation builds with no warnings/errors
if: steps.job-start.outputs.run_result != 'success'
run: |
sudo yum update -y
sudo yum install -y python3-pip make
sudo pip3 install -r docs/requirements.txt
make -C docs html
- name: Show error log from sphinx if failed
if: ${{ steps.job-start.outputs.run_result != 'success' && failure() }}
run: cat /tmp/sphinx-err*.log
- uses: ./.github/actions/job-end

View File

@ -1,7 +1,7 @@
# FireSim: Easy-to-use, Scalable, FPGA-accelerated Cycle-accurate Hardware Simulation
![FireSim Documentation Status](https://readthedocs.org/projects/firesim/badge/)
[![firesim](https://circleci.com/gh/firesim/firesim.svg?style=shield)](https://app.circleci.com/pipelines/github/firesim/firesim)
![Github Actions Status](https://github.com/firesim/firesim/actions/workflows/firesim-run-tests.yml/badge.svg)
## Contents
@ -15,7 +15,7 @@
## Using FireSim
To get started with using FireSim, see the tutorials on the FireSim documentation
site: https://docs.fires.im/.
site: https://docs.fires.im/.
Another good overview (in video format) is our tutorial from the Chisel Community Conference on [YouTube](https://www.youtube.com/watch?v=S3OriQnJXYQ).
@ -31,6 +31,7 @@ You can learn more about FireSim in the following places:
* **FireSim website**: https://fires.im
* **FireSim ISCA 2018 Paper**: [Paper PDF](https://sagark.org/assets/pubs/firesim-isca2018.pdf) | [IEEE Xplore](https://ieeexplore.ieee.org/document/8416816) | [ACM DL](https://dl.acm.org/citation.cfm?id=3276543) | [BibTeX](https://sagark.org/assets/pubs/firesim-isca2018.bib.txt) | Selected as one of IEEE Micros “Top Picks from Computer Architecture Conferences, 2018”.
* **FireSim documentation**: https://docs.fires.im
* **Scala API Documentation**: https://fires.im/firesim/latest/api/
* **Two-minute lightning talk from ISCA 2018** (FireSim simulating a datacenter): [YouTube](https://www.youtube.com/watch?v=4XwoSe5c8lY)
* **Chisel Community Conference Tutorial**: [YouTube](https://www.youtube.com/watch?v=S3OriQnJXYQ)
* **Updates/News**: [Changelog](/CHANGELOG.md) | [FireSim Blog](https://fires.im/blog/) | [Twitter](https://twitter.com/firesimproject)

63
deploy/awstools/awstools.py Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env python3
from __future__ import print_function
import random
@ -151,7 +153,7 @@ def construct_instance_market_options(instancemarket, spotinterruptionbehavior,
else:
assert False, "INVALID INSTANCE MARKET TYPE."
def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavior, spotmaxprice, blockdevices=None, tags=None, randomsubnet=False):
def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavior, spotmaxprice, blockdevices=None, tags=None, randomsubnet=False, user_data_file=None):
""" Launch count instances of type instancetype, optionally with additional
block devices mappings and instance tags
@ -192,30 +194,37 @@ def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavi
chosensubnet = subnets[startsubnet].subnet_id
try:
instance = ec2.create_instances(ImageId=f1_image_id,
EbsOptimized=True,
BlockDeviceMappings=(blockdevices + [
{
'DeviceName': '/dev/sdb',
'NoDevice': '',
},
]),
InstanceType=instancetype, MinCount=1, MaxCount=1,
NetworkInterfaces=[
{'SubnetId': chosensubnet,
'DeviceIndex':0,
'AssociatePublicIpAddress':True,
'Groups':[firesimsecuritygroup]}
],
KeyName=keyname,
TagSpecifications=([] if tags is None else [
{
'ResourceType': 'instance',
'Tags': [{ 'Key': k, 'Value': v} for k, v in tags.items()],
},
]),
InstanceMarketOptions=marketconfig
)
instance_args = {"ImageId":f1_image_id,
"EbsOptimized":True,
"BlockDeviceMappings":(blockdevices + [
{
'DeviceName': '/dev/sdb',
'NoDevice': '',
},
]),
"InstanceType":instancetype,
"MinCount":1,
"MaxCount":1,
"NetworkInterfaces":[
{'SubnetId': chosensubnet,
'DeviceIndex':0,
'AssociatePublicIpAddress':True,
'Groups':[firesimsecuritygroup]}
],
"KeyName":keyname,
"TagSpecifications":([] if tags is None else [
{
'ResourceType': 'instance',
'Tags': [{ 'Key': k, 'Value': v} for k, v in tags.items()],
},
]),
"InstanceMarketOptions":marketconfig,
}
if user_data_file is not None:
with open(user_data_file, "r") as f:
instance_args["UserData"] = ''.join(f.readlines())
instance = ec2.create_instances(**instance_args)
instances += instance
except client.exceptions.ClientError as e:
@ -472,6 +481,7 @@ def main(args):
parser.add_argument("--block_devices", type=yaml.safe_load, default=run_block_device_dict(), help="List of dicts with block device information. Used by \'launch\'.")
parser.add_argument("--tags", type=yaml.safe_load, default=run_tag_dict(), help="Dict of tags to add to instances. Used by \'launch\'.")
parser.add_argument("--filters", type=yaml.safe_load, default=run_filters_list_dict(), help="List of dicts used to filter instances. Used by \'terminate\'.")
parser.add_argument("--user_data_file", default=None, help="File path to use as user data (run on initialization). Used by \'launch\'.")
args = parser.parse_args(args)
if args.command == "launch":
@ -483,7 +493,8 @@ def main(args):
args.spot_max_price,
args.block_devices,
args.tags,
args.random_subnet)
args.random_subnet,
args.user_data_file)
instids = get_instance_ids_for_instances(insts)
print("Instance IDs: {}".format(instids))
wait_on_instance_launches(insts)

View File

@ -2,7 +2,7 @@
manager """
from time import strftime, gmtime
import ConfigParser
import configparser
import pprint
from runtools.runtime_config import RuntimeHWDB
@ -89,7 +89,7 @@ class GlobalBuildConfig:
self.args = args
global_build_configfile = ConfigParser.ConfigParser(allow_no_value=True)
global_build_configfile = configparser.ConfigParser(allow_no_value=True)
# make option names case sensitive
global_build_configfile.optionxform = str
global_build_configfile.read(args.buildconfigfile)
@ -111,9 +111,9 @@ class GlobalBuildConfig:
self.post_build_hook = global_build_configfile.get('afibuild', 'postbuildhook')
# this is a list of actual builds to run
builds_to_run_list = map(lambda x: x[0], global_build_configfile.items('builds'))
builds_to_run_list = list(map(lambda x: x[0], global_build_configfile.items('builds')))
build_recipes_configfile = ConfigParser.ConfigParser(allow_no_value=True)
build_recipes_configfile = configparser.ConfigParser(allow_no_value=True)
# make option names case sensitive
build_recipes_configfile.optionxform = str
build_recipes_configfile.read(args.buildrecipesconfigfile)
@ -167,7 +167,7 @@ class GlobalBuildConfig:
def get_build_instance_ips(self):
""" Return a list of all the build instance IPs, i.e. hosts to pass to
fabric. """
return map(lambda x: x.get_build_instance_private_ip(), self.builds_list)
return list(map(lambda x: x.get_build_instance_private_ip(), self.builds_list))
def get_builds_list(self):
return self.builds_list

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# PYTHON_ARGCOMPLETE_OK
# REQUIRES PYTHON2, because fabric requires python2
from __future__ import with_statement, print_function
import sys
import os
@ -83,7 +81,7 @@ def managerinit():
rootLogger.debug(m)
rootLogger.debug(m.stderr)
useremail = raw_input("If you are a new user, supply your email address [abc@xyz.abc] for email notifications (leave blank if you do not want email notifications): ")
useremail = input("If you are a new user, supply your email address [abc@xyz.abc] for email notifications (leave blank if you do not want email notifications): ")
if useremail != "":
subscribe_to_firesim_topic(useremail)
else:
@ -118,7 +116,7 @@ def buildafi(globalbuildconf):
def terminate_instances_handler(sig, frame):
""" Handler that prompts to terminate build instances if you press ctrl-c. """
rootLogger.info("You pressed ctrl-c, so builds have been killed.")
userconfirm = raw_input("Do you also want to terminate your build instances? Type 'yes' to do so.\n")
userconfirm = input("Do you also want to terminate your build instances? Type 'yes' to do so.\n")
if userconfirm == "yes":
globalbuildconf.terminate_all_build_instances()
rootLogger.info("Instances terminated. Please confirm in your AWS Management Console.")

View File

@ -6,12 +6,13 @@ import pprint
import logging
import datetime
from switch_model_config import *
from firesim_topology_core import *
from utils import MacAddress
from runtools.switch_model_config import *
from runtools.firesim_topology_core import *
from runtools.utils import MacAddress
from fabric.api import *
from colorama import Fore, Style
import types
from functools import reduce
from util.streamlogger import StreamLogger
@ -191,7 +192,7 @@ class FireSimTopologyWithPasses:
# Filter out FireSimDummyServerNodes for actually deploying.
# Infrastructure after this point will automatically look at the
# FireSimDummyServerNodes if a FireSimSuperNodeServerNode is used
downlinknodes = map(lambda x: x.get_downlink_side(), [downlink for downlink in switch.downlinks if not isinstance(downlink.get_downlink_side(), FireSimDummyServerNode)])
downlinknodes = list(map(lambda x: x.get_downlink_side(), [downlink for downlink in switch.downlinks if not isinstance(downlink.get_downlink_side(), FireSimDummyServerNode)]))
if all([isinstance(x, FireSimSwitchNode) for x in downlinknodes]):
# all downlinks are switches
self.run_farm.m4_16s[m4_16s_used].add_switch(switch)
@ -465,7 +466,7 @@ class FireSimTopologyWithPasses:
break
# If AutoILA is disabled, use the following condition
elif "No Sockets found" in screenoutput:
break
break
time.sleep(1)
execute(screens, hosts=all_runfarm_ips)
@ -501,22 +502,22 @@ class FireSimTopologyWithPasses:
instancestate_map = dict()
if terminateoncompletion:
for instip, instdata in instancestates.iteritems():
for instip, instdata in instancestates.items():
# if terminateoncompletion and all sims are terminated, the inst must have been terminated
instancestate_map[instip] = all([x[1] for x in instdata['sims'].iteritems()])
instancestate_map[instip] = all([x[1] for x in instdata['sims'].items()])
else:
instancestate_map = {inst: False for inst in instancestates.keys()}
switchstates = []
for instip, instdata in instancestates.iteritems():
for switchname, switchcompleted in instdata['switches'].iteritems():
for instip, instdata in instancestates.items():
for switchname, switchcompleted in instdata['switches'].items():
switchstates.append({'hostip': instip,
'switchname': switchname,
'running': not switchcompleted})
simstates = []
for instip, instdata in instancestates.iteritems():
for simname, simcompleted in instdata['sims'].iteritems():
for instip, instdata in instancestates.items():
for simname, simcompleted in instdata['sims'].items():
simstates.append({'hostip': instip,
'simname': simname,
'running': not simcompleted})
@ -532,7 +533,7 @@ class FireSimTopologyWithPasses:
totalsims = len(simstates)
totalinsts = len(instancestate_map.keys())
runningsims = len([x for x in simstates if x['running']])
runninginsts = len([x for x in instancestate_map.iteritems() if not x[1]])
runninginsts = len([x for x in instancestate_map.items() if not x[1]])
# clear the screen
rootLogger.info('\033[2J')

View File

@ -337,7 +337,7 @@ class RunFarm:
if not forceterminate:
# --forceterminate was not supplied, so confirm with the user
userconfirm = raw_input("Type yes, then press enter, to continue. Otherwise, the operation will be cancelled.\n")
userconfirm = input("Type yes, then press enter, to continue. Otherwise, the operation will be cancelled.\n")
else:
userconfirm = "yes"
@ -384,14 +384,17 @@ class InstanceDeployManager:
def get_and_install_aws_fpga_sdk(self):
""" Installs the aws-sdk. This gets us access to tools to flash the fpga. """
# TODO: we checkout a specific version of aws-fpga here, in case upstream
# master is bumped. But now we have to remember to change AWS_FPGA_FIRESIM_UPSTREAM_VERSION
# when we bump our stuff. Need a better way to do this.
AWS_FPGA_FIRESIM_UPSTREAM_VERSION = "6c707ab4a26c2766b916dad9d40727266fa0e4ef"
self.instance_logger("""Installing AWS FPGA SDK on remote nodes. Upstream hash: {}""".format(AWS_FPGA_FIRESIM_UPSTREAM_VERSION))
with prefix('cd ../'), \
StreamLogger('stdout'), \
StreamLogger('stderr'):
# use local version of aws_fpga on runfarm nodes
aws_fpga_upstream_version = local('git -C platforms/f1/aws-fpga describe --tags --always --dirty', capture=True)
if "-dirty" in aws_fpga_upstream_version:
rootLogger.critical("Unable to use local changes to aws-fpga. Continuing without them.")
self.instance_logger("""Installing AWS FPGA SDK on remote nodes. Upstream hash: {}""".format(aws_fpga_upstream_version))
with warn_only(), StreamLogger('stdout'), StreamLogger('stderr'):
run('git clone https://github.com/aws/aws-fpga')
run('cd aws-fpga && git checkout ' + AWS_FPGA_FIRESIM_UPSTREAM_VERSION)
run('cd aws-fpga && git checkout ' + aws_fpga_upstream_version)
with cd('/home/centos/aws-fpga'), StreamLogger('stdout'), StreamLogger('stderr'):
run('source sdk_setup.sh')
@ -547,7 +550,7 @@ class InstanceDeployManager:
self.instance_logger("Starting Vivado virtual JTAG.")
with StreamLogger('stdout'), StreamLogger('stderr'):
run("""screen -S virtual_jtag -d -m bash -c "script -f -c 'sudo fpga-start-virtual-jtag -P 10201 -S 0'"; sleep 1""")
def kill_ila_server(self):
""" Kill the vivado hw_server and virtual jtag """
with warn_only(), StreamLogger('stdout'), StreamLogger('stderr'):

View File

@ -4,7 +4,7 @@ simulation tasks. """
from __future__ import print_function
from time import strftime, gmtime
import ConfigParser
import configparser
import pprint
import logging
@ -223,7 +223,7 @@ class RuntimeHWDB:
as endpoints on the simulation. """
def __init__(self, hardwaredbconfigfile):
agfidb_configfile = ConfigParser.ConfigParser(allow_no_value=True)
agfidb_configfile = configparser.ConfigParser(allow_no_value=True)
agfidb_configfile.read(hardwaredbconfigfile)
agfidb_dict = {s:dict(agfidb_configfile.items(s)) for s in agfidb_configfile.sections()}
@ -240,7 +240,7 @@ class InnerRuntimeConfiguration:
""" Pythonic version of config_runtime.ini """
def __init__(self, runtimeconfigfile, configoverridedata):
runtime_configfile = ConfigParser.ConfigParser(allow_no_value=True)
runtime_configfile = configparser.ConfigParser(allow_no_value=True)
runtime_configfile.read(runtimeconfigfile)
runtime_dict = {s:dict(runtime_configfile.items(s)) for s in runtime_configfile.sections()}

View File

@ -14,7 +14,7 @@
[firesim-rocket-quadcore-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=WithNIC_DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimHighPerfConfigTweaks_chipyard.QuadRocketConfig
PLATFORM_CONFIG=WithAutoILA_F90MHz_BaseF1Config
PLATFORM_CONFIG=F90MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -23,7 +23,7 @@ deploytriplet=None
[firesim-rocket-quadcore-no-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.QuadRocketConfig
PLATFORM_CONFIG=WithAutoILA_F140MHz_BaseF1Config
PLATFORM_CONFIG=F140MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -32,7 +32,7 @@ deploytriplet=None
[firesim-boom-singlecore-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=WithNIC_DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimHighPerfConfigTweaks_chipyard.LargeBoomConfig
PLATFORM_CONFIG=WithAutoILA_F65MHz_BaseF1Config
PLATFORM_CONFIG=F65MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -41,7 +41,7 @@ deploytriplet=None
[firesim-boom-singlecore-no-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.LargeBoomConfig
PLATFORM_CONFIG=WithAutoILA_F75MHz_BaseF1Config
PLATFORM_CONFIG=F75MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -50,7 +50,7 @@ deploytriplet=None
[firesim-cva6-singlecore-no-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimConfigTweaks_chipyard.CVA6Config
PLATFORM_CONFIG=WithAutoILA_F90MHz_BaseF1Config
PLATFORM_CONFIG=F90MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -59,7 +59,7 @@ deploytriplet=None
[firesim-rocket-singlecore-gemmini-no-nic-l2-llc4mb-ddr3]
DESIGN=FireSim
TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimConfigTweaks_chipyard.GemminiRocketConfig
PLATFORM_CONFIG=WithAutoILA_F110MHz_BaseF1Config
PLATFORM_CONFIG=F30MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -68,7 +68,7 @@ deploytriplet=None
[firesim-boom-singlecore-no-nic-l2-llc4mb-ddr3-ramopts]
DESIGN=FireSim
TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.LargeBoomConfig
PLATFORM_CONFIG=WithAutoILA_MCRams_F90MHz_BaseF1Config
PLATFORM_CONFIG=MCRams_F90MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None
@ -77,7 +77,7 @@ deploytriplet=None
[firesim-supernode-rocket-singlecore-nic-l2-lbp]
DESIGN=FireSim
TARGET_CONFIG=WithNIC_SupernodeFireSimRocketConfig
PLATFORM_CONFIG=WithAutoILA_F85MHz_BaseF1Config
PLATFORM_CONFIG=F85MHz_BaseF1Config
instancetype=z1d.2xlarge
deploytriplet=None

View File

@ -8,7 +8,7 @@ import boto3
from botocore.stub import Stubber
from moto import mock_sns
from mock import patch
from unittest.mock import patch
import pytest
from pytest import raises

View File

@ -7,7 +7,7 @@ which has no license associated with it.
"""
import sys
import logging
import cStringIO
import io
class StreamLogger(object):
@ -37,7 +37,7 @@ class StreamLogger(object):
self.__name = name
self.__stream = getattr(sys, name)
self.__logger = logger or logging.getLogger()
self.__buffer = cStringIO.StringIO()
self.__buffer = io.StringIO()
self.__unbuffered = unbuffered
self.__flush_on_new_line = flush_on_new_line

View File

@ -21,44 +21,41 @@ gapbs: input = graph500
$(GAP_DIR)/overlay/$(input):
cd $(GAP_DIR) && ./gen_run_scripts.sh --binaries --input $(input)
gapbs: gapbs.json $(GAP_DIR)/overlay/$(input)
gapbs: gapbs.json $(GAP_DIR)/overlay/$(input)
mkdir -p $@
cp $(BASE_LINUX) $@/bbl-vmlinux
python gen-benchmark-rootfs.py -w $< -r -b $(BASE_IMAGE) \
python3 gen-benchmark-rootfs.py -w $< -r -b $(BASE_IMAGE) \
-s $(GAP_DIR)/overlay/$(input) \
memcached-thread-imbalance:
mkdir -p $@
sudo yum -y install gengetopt
sudo pip2 install matplotlib
sudo pip2 install pandas
cd $@ && git submodule update --init mutilate-loadgen-riscv-release
cd $@/mutilate-loadgen-riscv-release && ./build.sh
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/mutilate-loadgen-riscv-release/overlay
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/mutilate-loadgen-riscv-release/overlay
bw-test-two-instances: bw-test-two-instances.json
cd ../../sw/network-benchmarks && python build-bw-test.py -n 8
cd ../../sw/network-benchmarks && python3 build-bw-test.py -n 8
cp ../../sw/network-benchmarks/testbuild/*.riscv $@
bw-test-one-instance: bw-test-one-instance.json
cd ../../sw/network-benchmarks && python build-bw-test.py -n 4
cd ../../sw/network-benchmarks && python3 build-bw-test.py -n 4
cp ../../sw/network-benchmarks/testbuild/*.riscv $@
ping-latency:
mkdir -p $@
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
simperf-test:
mkdir -p $@
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
linux-poweroff:
mkdir -p $@/overlay
cd ../../sw/check-rtc && make print-mcycle-linux
cp ../../sw/check-rtc/print-mcycle-linux $@/overlay/
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
simperf-test-scale: simperf-test
@ -69,7 +66,7 @@ flash-stress: simperf-test-latency
iperf3: iperf3.json
mkdir -p $@
cd $@ && ln -sf ../$(BASE_LINUX) bbl-vmlinux
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE)
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE)
check-rtc:
cd ../../sw/check-rtc && make check-rtc
@ -79,14 +76,14 @@ check-rtc-linux:
cd ../../sw/check-rtc && make check-rtc-linux
cp ../../sw/check-rtc/check-rtc-linux $@/overlay
cd $@ && ln -sf ../$(BASE_LINUX) bbl-vmlinux
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay
checksum-test:
cd ../../target-design/chipyard/tests && make checksum.riscv
ccbench-cache-sweep:
cd ccbench-cache-sweep/ccbench/caches && make ARCH=riscv
python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/
python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/
fc-test:
cd ../../sw/network-benchmarks/fc-test && make

View File

@ -14,9 +14,9 @@ q = f.readlines()
f.close()
q = filter(lambda x: x.startswith('App:'), q)
q = map(lambda x: x.strip().split(","), q)
q = map(lambda x: list(map(lambda z: z.split(":"), x)), q)
q = list(filter(lambda x: x.startswith('App:'), q))
q = list(map(lambda x: x.strip().split(","), q))
q = list(map(lambda x: list(map(lambda z: z.split(":"), x)), q))
def arr_to_dict(q):
@ -29,9 +29,9 @@ def arr_to_dict(q):
as_dict.append(d)
return as_dict
cacheline_stride_bmark = filter(lambda x: ['RunType', '[16]'] in x, q)
unit_stride_bmark = filter(lambda x: ['RunType', '[1]'] in x, q)
random_bmark = filter(lambda x: ['RunType', '[0]'] in x, q)
cacheline_stride_bmark = list(filter(lambda x: ['RunType', '[16]'] in x, q))
unit_stride_bmark = list(filter(lambda x: ['RunType', '[1]'] in x, q))
random_bmark = list(filter(lambda x: ['RunType', '[0]'] in x, q))
def data_from_full_dict(array_of_dict):
times = []

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
import argparse
import json
@ -49,7 +49,7 @@ def copy_base_rootfs(base_rootfs, dest):
os.makedirs(BUILD_DIR)
except OSError:
pass
print "Copying base rootfs {} to {}".format(base_rootfs, dest)
print("Copying base rootfs {} to {}".format(base_rootfs, dest))
shutil.copy2(base_rootfs, dest)
def mount_rootfs(rootfs):
@ -60,7 +60,7 @@ def mount_rootfs(rootfs):
rc = subprocess.check_output(["sudo", "mount", "-t", EXT_TYPE, rootfs, MOUNT_POINT])
def cp_target(src, target_dest):
print "Copying src: {} to {} in target filesystem.".format(src, target_dest)
print("Copying src: {} to {} in target filesystem.".format(src, target_dest))
if not os.path.isdir(target_dest):
dirname = os.path.dirname(target_dest)
else:
@ -84,7 +84,7 @@ def generate_init_script(command):
init_script_body = init_script_head + " " + command + init_script_tail
temp_script = BUILD_DIR + "/temp"
with open(temp_script, 'wb') as f:
with open(temp_script, 'w') as f:
f.write(init_script_body)
cp_target(temp_script, INIT_SCRIPT_NAME)
@ -106,7 +106,7 @@ class Workload:
self.outputs = outputs
def generate_rootfs(self, base_rootfs, overlay, gen_init, output_dir):
print "\nGenerating a Rootfs image for " + self.name
print("\nGenerating a Rootfs image for " + self.name)
dest_rootfs = output_dir + "/" + self.name + "." + EXT_TYPE
copy_base_rootfs(base_rootfs, dest_rootfs)

@ -1 +1 @@
Subproject commit 150a77698e8e786b8a87c14ae383889a1c24df67
Subproject commit 873e0e5ca10f8a3f016023562a3d15e91af9229b

View File

@ -11,7 +11,7 @@ import os
basedir = sys.argv[1] + "/"
files = map(lambda x: basedir + x, sorted(os.listdir(basedir), key=int))
files = list(map(lambda x: basedir + x, sorted(os.listdir(basedir), key=int)))
def process_uartlog(uartlogpath):
""" process the log and then report the mean RTT for this link latency """
@ -52,11 +52,11 @@ def get_average_rtt_from_file(basedirname):
return [link_latency_us, measured_rtt_in_us, ideal_rtt_in_us]
resultarray = map(get_average_rtt_from_file, files)
resultarray = list(map(get_average_rtt_from_file, files))
link_latency = map(lambda x: x[0], resultarray)
measured_rtt = map(lambda x: x[1], resultarray)
ideal_rtt = map(lambda x: x[2], resultarray)
link_latency = list(map(lambda x: x[0], resultarray))
measured_rtt = list(map(lambda x: x[1], resultarray))
ideal_rtt = list(map(lambda x: x[2], resultarray))
print(resultarray)

View File

@ -40,7 +40,7 @@ do
mv $originalfilename $resultsdir/$i
done
python $ORIGDIR/bw-test-two-instances/bw-test-graph.py $(pwd)/$resultsdir
python3 $ORIGDIR/bw-test-two-instances/bw-test-graph.py $(pwd)/$resultsdir
cd $ORIGDIR
cd ../../

View File

@ -39,7 +39,7 @@ do
mv $originalfilename $resultsdir/$i
done
python $ORIGDIR/ping-latency/ping-latency-graph.py $(pwd)/$resultsdir
python3 $ORIGDIR/ping-latency/ping-latency-graph.py $(pwd)/$resultsdir
firesim terminaterunfarm -c workloads/ping-latency-config.ini --forceterminate

View File

@ -39,7 +39,7 @@ do
mv $originalfilename $resultsdir/$i
done
python $ORIGDIR/simperf-test-latency/simperf-test-results.py $(pwd)/$resultsdir
python3 $ORIGDIR/simperf-test-latency/simperf-test-results.py $(pwd)/$resultsdir
firesim terminaterunfarm -c workloads/simperf-test-latency-config.ini --forceterminate

View File

@ -72,4 +72,4 @@ loopfunc 8 0 0
loopfunc 4 1 0
python $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir
python3 $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir

View File

@ -72,4 +72,4 @@ loopfunc 2 0 0
loopfunc 1 1 0
python $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir
python3 $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir

View File

@ -11,7 +11,7 @@ import os
basedir = sys.argv[1] + "/"
files = map(lambda x: basedir + x, os.listdir(basedir))
files = list(map(lambda x: basedir + x, os.listdir(basedir)))
def process_uartlog(uartlogpath):
""" process the log and then report the mean RTT for this link latency """
@ -62,10 +62,10 @@ def get_simperf_from_file(basedirname):
return [link_latency_us, simperf_mhz]
resultarray = map(get_simperf_from_file, files)
resultarray = list(map(get_simperf_from_file, files))
link_latency = map(lambda x: x[0], resultarray)
simperf_mhz = map(lambda x: x[1][1], resultarray)
link_latency = list(map(lambda x: x[0], resultarray))
simperf_mhz = list(map(lambda x: x[1][1], resultarray))
resultarray = zip(link_latency, simperf_mhz)

View File

@ -11,7 +11,7 @@ import os
basedir = sys.argv[1] + "/"
files = map(lambda x: basedir + x, os.listdir(basedir))
files = list(map(lambda x: basedir + x, os.listdir(basedir)))
def extract_stats_from_uartlog(uartlogpath):
""" read a uartlog and get sim perf results """
@ -44,10 +44,10 @@ def get_simperf_from_file(basedirname):
return [numnodes, simperf_mhz]
resultarray = map(get_simperf_from_file, files)
resultarray = list(map(get_simperf_from_file, files))
numnodes = map(lambda x: x[0], resultarray)
simperf_mhz = map(lambda x: x[1][1], resultarray)
numnodes = list(map(lambda x: x[0], resultarray))
simperf_mhz = list(map(lambda x: x[1][1], resultarray))
resultarray = zip(numnodes, simperf_mhz)

1
docs/.gitignore vendored
View File

@ -1 +1,2 @@
_build
warnings.txt

View File

@ -9,7 +9,7 @@ obvious if it's a bug in the target, or somewhere in the host. To make it easier
identify the problem, the simulation driver includes a polling watchdog that
tracks for simulation progress, and periodically updates an output file,
``heartbeat.csv``, with a target cycle count and a timestamp. When debugging
these issues, we always encourage the use of MIDAS-level simulation to try
these issues, we always encourage the use of meta-simulation to try
reproducing the failure if possible. We outline three common cases in the
section below.

View File

@ -13,11 +13,17 @@ and provided and interface for setting trigger and viewing samples waveforms
from the FPGA. For more information about ILAs, please refer to the Xilinx
guide on the topic.
MIDAS, in its ``targetutils`` package, provides annotations for labeling
The ``midas.targetutils`` package provides annotations for labeling
signals directly in the Chisel source. These will be consumed by a downstream
FIRRTL pass which wires out the annotated signals, and binds them to an
appropriately sized ILA instance.
Enabling AutoILA
----------------
To enable AutoILA, mixin `WithAutoILA` must be prepended to the
`PLATFORM_CONFIG`. Prior to version 1.13, this was done by default.
Annotating Signals
------------------------
@ -35,7 +41,7 @@ vararg of chisel3.Data. Invoke it as follows:
FpgaDebug(out1, in1)
}
You can annotate signals throughout FireSim, including in MIDAS and
You can annotate signals throughout FireSim, including in Golden Gate
Rocket-Chip Chisel sources, with the only exception being the Chisel3 sources
themselves (eg. in Chisel3.util.Queue).

View File

@ -84,11 +84,11 @@ The commit log trace will by default print to the ``uartlog``.
However, you can avoid printing it out by changing ``verbose == false`` in the ``dromajo_cosim.cpp`` file
located in ``$CHIPYARD/tools/dromajo/dromajo-src/src/`` folder.
Troubleshooting Dromajo Simulations with MIDAS Simulations
Troubleshooting Dromajo Simulations with Meta-Simulations
----------------------------------------------------------
If FPGA simulation fails with Dromajo, you can use MIDAS-level simulation to determine if your Dromajo setup is correct.
First refer to :ref:`Debugging & Testing with RTL Simulation` for more information on MIDAS-level simulation.
If FPGA simulation fails with Dromajo, you can use meta-simulation to determine if your Dromajo setup is correct.
First refer to :ref:`meta-simulation` for more information on meta-simulation.
The main difference between those instructions and simulations with Dromajo is that you need to manually point to the ``dtb``, ``rom``, and binary files when invoking the simulator.
Here is an example of a ``make`` command that can be run to check for a correct setup.

View File

@ -1,6 +1,7 @@
.. _meta-simulation:
Debugging & Testing with Meta-Simulation
=========================================
.. _meta-simulation:
When we speak of RTL simulation in FireSim, we are generally referring to
`meta-simulation`: simulating the FireSim simulator's RTL, typically using VCS or
@ -23,10 +24,10 @@ ones. This illustrated in the chart below.
====== ===== ======= ========= ============= =============
Type Waves VCS Verilator Verilator -O1 Verilator -O2
====== ===== ======= ========= ============= =============
Target Off 4.8 kHz 3.9 kHz 6.6 kHz N/A
Target On 0.8 kHz 3.0 kHz 5.1 kHz N/A
Meta Off 3.8 kHz 2.4 kHz 4.5 kHz 5.3 KHz
Meta On 2.9 kHz 1.5 kHz 2.7 kHz 3.4 KHz
Target Off 4.8 kHz 3.9 kHz 6.6 kHz N/A
Target On 0.8 kHz 3.0 kHz 5.1 kHz N/A
Meta Off 3.8 kHz 2.4 kHz 4.5 kHz 5.3 KHz
Meta On 2.9 kHz 1.5 kHz 2.7 kHz 3.4 KHz
====== ===== ======= ========= ============= =============
Note that using more aggressive optimization levels when compiling the
@ -137,7 +138,7 @@ Module Hierarchy
To build out a simulator, Golden Gate adds multiple layers of module hierarchy to the target
design and performs additional hierarchy mutations to implement bridges and
resource optimizations. Meta-simulation uses the ``FPGATop`` module as the
top-level module, which excludes the platform shim layer (``F1Shim``, for EC2 F1).
top-level module, which excludes the platform shim layer (``F1Shim``, for EC2 F1).
The original top-level of the input design is nested three levels below FPGATop:
.. figure:: /img/metasim-module-hierarchy.png

View File

@ -14,7 +14,7 @@ Restrictions on Target RTL
Current limitations in Golden Gate place the following restrictions on the (FIR)RTL that can be
transformed and thus used in FireSim:
#. The top-level module must have no inputs or outputs. Input stimulus and output capture must be
#. The top-level module must have no inputs or outputs. Input stimulus and output capture must be
implemented using target RTL or target-to-host Bridges.
#. All target clocks must be generated by a single ``RationalClockBridge``.
#. Black boxes must be "clock-gateable" by replacing its input clock with a gated equivalent which will be used
@ -23,7 +23,7 @@ transformed and thus used in FireSim:
a. As a consequence, target clock-gating cannot be implemented using black-box primitives, and must instead be modeled by
adding clock-enables to all state elements of the gated clock domain (i.e., by adding an enable or feedback mux on registers to
conditionally block updates, and by gating write-enables on memories).
#. Asynchronous reset must only be implemented using Rocket Chip's black-box async reset.
#. Asynchronous reset must only be implemented using Rocket Chip's black-box async reset.
These are replaced with synchronously reset registers using a FIRRTL transformation.
.. _verilog-ip:
@ -39,7 +39,7 @@ Verilog Blocks
<https://chipyard.readthedocs.io/en/latest/Customization/Incorporating-Verilog-Blocks.html>`_
section of the Chipyard documentation.
#. For the transform to work, the Chisel Blackbox that wraps the Verilog IP must have input clocks
#. For the transform to work, the Chisel Blackbox that wraps the Verilog IP must have input clocks
that can safely be clock-gated.
#. The compiler that produces the decoupled simulator ("FAME Transform") automatically recognizes
such blackboxes inside the target design.
@ -132,7 +132,7 @@ Projects have the following directory structure:
├-Makefile # Top-level makefile for projects where FireSim is the top-level repo
├-Makefrag # Target-agnostic makefrag, with recipes to generate drivers and RTL simulators
├-src/main/scala/{target-project}/
│ └─Makefrag # Defines target-specific make variables and recipes.
│ └─Makefrag # Defines target-specific make variables and recipes.
├-src/main/cc/{target-project}/
│ ├─{driver-csrcs}.cc # The target's simulation driver, and sofware-model sources
│ └─{driver-headers}.h
@ -147,8 +147,8 @@ Specifying A Target Instance
To generate a specific instance of a target, the build system leverages four Make variables:
1. ``TARGET_PROJECT``: this points the Makefile (`sim/Makefile`) at the right
target-specific Makefrag, which defines the generation and MIDAS-level
software-simulation recipes. The makefrag for the default target project is
target-specific Makefrag, which defines the generation and meta-simulation
software recipes. The makefrag for the default target project is
defined at ``sim/src/main/makefrag/firesim``.
2. ``DESIGN``: the name of the top-level Chisel module to generate (a Scala class name). These are defined
@ -164,7 +164,7 @@ To generate a specific instance of a target, the build system leverages four Mak
Common platform configs are described in ``firesim-lib/sim/src/main/scala/configs/CompilerConfigs.scala``).
``TARGET_CONFIG`` and ``PLATFORM_CONFIG`` are strings that are used to construct a
``Config`` instance (derives from RocketChip's parameterization system, ``Config``, see
``Config`` instance (derives from RocketChip's parameterization system, ``Config``, see
`freechips.rocketchip.config
<https://github.com/freechipsproject/rocket-chip/blob/master/src/main/scala/config/Config.scala>`_). These strings are of the form
"{..._}{<Class Name>\_}<Class Name>". Only the final, base class name is
@ -185,7 +185,7 @@ compound Config instance.
With this scheme, you don't need to define a Config class for every instance you
wish to generate. We use this scheme to specify FPGA frequencies (eg.
"BaseF1Config_F90MHz") in manager build recipes, but it's also very useful for doing
sweeping over a parameterization space.
sweeping over a parameterization space.
**Note that the precedence of Configs decreases from left to right in a string**. Appending a config to an existing one will only have an effect if it
sets a field not already set in higher precendence Configs. For example, "BaseF1Config_F90MHz" is equivalent to
@ -225,7 +225,7 @@ expect is to open the scala REPL, instantiate an instance of the desired
Rocket Chip Generator-based SoCs (firesim project)
--------------------------------------------------
Using the Make variables listed above, we give examples of generating different targets using
Using the Make variables listed above, we give examples of generating different targets using
the default Rocket Chip-based target project.
-----------------

View File

@ -6,7 +6,7 @@ Manager Environment Variables
This page contains a centralized reference for the environment variables used
by the manager.
.. _config-runtime:
.. _runfarm-prefix:
``FIRESIM_RUNFARM_PREFIX``
--------------------------

View File

@ -80,16 +80,16 @@ This directory will contain:
----------------------
This command can be used to run only steps 9 & 10 from an aborted ``firesim buildafi`` that has been
manually corrected. ``firesim tar2afi`` assumes that you have a
manually corrected. ``firesim tar2afi`` assumes that you have a
``firesim/deploy/results-build/LAUNCHTIME-CONFIG_TRIPLET-BUILD_NAME/cl_firesim``
directory tree that can be submitted to the AWS backend for conversion to an AFI.
When using this command, you need to also provide the ``--launchtime LAUNCHTIME`` cmdline argument,
specifying an already existing LAUNCHTIME.
specifying an already existing LAUNCHTIME.
This command will run for the configurations specified in :ref:`config-build` and
This command will run for the configurations specified in :ref:`config-build` and
:ref:`config-build-recipes` as with :ref:`firesim-buildafi`. It is likely that you may want
to comment out ``BUILD_NAME`` that successfully completed :ref:`firesim-builafi` before
to comment out ``BUILD_NAME`` that successfully completed :ref:`firesim-buildafi` before
running this command.
@ -282,7 +282,7 @@ workload configuration (see the :ref:`defining-custom-workloads` section).
For
non-networked simulations, it will wait for ALL simulations to complete (copying
back results as each workload completes), then exit.
back results as each workload completes), then exit.
For
globally-cycle-accurate networked simulations, the global simulation will stop

View File

@ -135,3 +135,9 @@ Then, you can move the cursor over something you want to jump to and hit
``ctrl-]`` to jump to the definition and ``ctrl-t`` to jump back out. E.g. in
top-level configurations in FireSim, you can jump all the way down through the
Rocket Chip codebase and even down to Chisel.
Using FireSim CI
----------------
For more information on how to deal with the FireSim CI and how to run FPGA simulations in the CI,
refer to the the ``CI_README.md`` under the ``.github/`` directory.

View File

@ -79,7 +79,7 @@ directory name the same. In this case, we have set all of them to
Next, the ``common_bootbinary`` field represents the binary that the simulations
in this workload are expected to boot from. The manager will copy this binary
for each of the nodes in the simulation (each gets its own copy). The ``common_bootbinary`` path is
for each of the nodes in the simulation (each gets its own copy). The ``common_bootbinary`` path is
relative to the workload's directory, in this case
``firesim/deploy/workloads/linux-uniform``. You'll notice in the above output
from ``ls -la`` that this is actually just a symlink to ``br-base-bin`` that
@ -123,7 +123,7 @@ be fixed in a future release.
Non-uniform Workload JSON (explicit job per simulated node)
---------------------------------------------------------------
Now, we'll look at the ``ping-latency`` workload, which explicitly defines a
Now, we'll look at the ``ping-latency`` workload, which explicitly defines a
job per simulated node.
.. include:: /../deploy/workloads/ping-latency.json
@ -193,10 +193,10 @@ see in the ``ping-latency`` directory.
::
[ from the workloads/ directory ]
python gen-benchmark-rootfs.py -w ping-latency.json -r -b ../../sw/firesim-software/images/br-base.img -s ping-latency/overlay
./gen-benchmark-rootfs.py -w ping-latency.json -r -b ../../sw/firesim-software/images/br-base.img -s ping-latency/overlay
Notice that we tell this script where the json file lives, where the base rootfs image is, and where we expect to find files
that we want to include in the generated disk images. This script will take care of the rest and we'll end up with
that we want to include in the generated disk images. This script will take care of the rest and we'll end up with
``idler-[1-6].ext2``, ``pingee.ext2``, and ``pinger.ext2``!
You'll notice a Makefile in the ``workloads/`` directory -- it contains many

View File

@ -127,7 +127,7 @@ from the BridgeModule may deadlock the simulator.
Registering the Driver
++++++++++++++++++++++
With the Bridge Driver implemented, we now have to register it in the main simulator
With the Bridge Driver implemented, we now have to register it in the main simulator
simulator class defined in ``sim/src/main/cc/firesim/firesim_top.cc``. Here, we
rely on the C preprocessor macros to instantiate the bridge driver only when
the corresponding BridgeModule is present:
@ -157,4 +157,4 @@ Here the main order of business is to add header and source files to
:end-before: DOC include end: Bridge Build System Changes
That's it! At this point you should be able to both test your bridge in software
simulation using MIDAS-level simulation, or deploy it to an FPGA.
simulation using meta-simulation, or deploy it to an FPGA.

View File

@ -108,17 +108,18 @@ See
https://docs.aws.amazon.com/cli/latest/userguide/tutorial-ec2-ubuntu.html#configure-cli-launch-ec2
for more about aws configure. Within the prompt, you should specify the same region that you chose
above (one of ``us-east-1``, ``us-west-2``, ``eu-west-1``) and set the default
output format to ``json``. You will need to generate an AWS access key in the "Security Credentials" menu of your AWS settings (as instructed in https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys ).
output format to ``json``. You will need to generate an AWS access key in the "Security Credentials" menu of your AWS settings (as instructed in https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys ).
Again on the ``t2.nano`` instance, do the following:
::
sudo yum -y install python-pip
sudo pip install boto3
sudo pip install --upgrade awscli
sudo yum install -y python36-pip
sudo pip3 install --upgrade pip
sudo python3 -m pip install boto3
sudo python3 -m pip install --upgrade awscli
wget https://raw.githubusercontent.com/firesim/firesim/master/scripts/aws-setup.py
python aws-setup.py
./aws-setup.py
This will create a VPC named ``firesim`` and a security group named
``firesim`` in your account.

View File

@ -2,8 +2,8 @@
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXOPTS = -w warnings.txt -n -W
SPHINXBUILD = python3 -msphinx
SPHINXPROJ = FireSim
SOURCEDIR = .
BUILDDIR = _build

View File

@ -1,12 +1,12 @@
Building Docs
--------------
sudo pip install -r requirements.txt
sudo python3 -m pip install -r requirements.txt
make html
Look in the `_build/html` directory for output. You can also run
Look in the `_build/html` directory for output. You can also run
python -m SimpleHTTPServer
python3 -m SimpleHTTPServer
To get a proper locally-hosted version.

View File

@ -108,7 +108,7 @@ html_static_path = ['_static']
#
# html_sidebars = {}
html_logo = '_static/images/firesim_logo_small.png'
html_logo = '_static/firesim_logo_small.png'
# -- Options for HTMLHelp output ---------------------------------------------

3
regression/aws-ec2-tests/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
ip_address
machine-launch-script.sh

View File

@ -0,0 +1,8 @@
Assumes the following:
- Are running on an AWS instance (needed to access private IPs + username centos)
- Have a working AWS setup (ran aws configure)
- Have a specific hash to test (hash must be accessible from the mainline firesim repo)
1. Launch manager using script (should log the IP address in a file to use)
2. Run a specific regression (up to the user to ensure that it ends properly)
3. Terminate manager instance

View File

@ -0,0 +1,11 @@
#!/bin/bash
set -ex
set -o pipefail
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/defaults.sh
run "cd firesim/ && source sourceme-f1-manager.sh && firesim buildafi"
echo "Success"

View File

@ -0,0 +1,32 @@
IP_ADDR_FILE=$SCRIPT_DIR/ip_address
parse_ip_address () {
IP_ADDR=$(grep -E -o "192\.168\.[0-9]{1,3}\.[0-9]{1,3}" $IP_ADDR_FILE | head -n 1)
IP_ADDR="centos@$IP_ADDR"
}
FIRESIM_PEM_FILE=~/firesim.pem
copy () {
rsync -avzp -e "ssh -o StrictHostKeyChecking=no -i $FIRESIM_PEM_FILE" --exclude '.git' $1 $2
}
copy_no_sym () {
rsync -avzpL -e "ssh -o StrictHostKeyChecking=no -i $FIRESIM_PEM_FILE" --exclude '.git' $1 $2
}
run () {
if [ -z $IP_ADDR ]; then
parse_ip_address
fi
ssh -i $FIRESIM_PEM_FILE -o "StrictHostKeyChecking no" -t $IP_ADDR "bash -l -c '$@'"
}
run_script () {
if [ -z $IP_ADDR ]; then
parse_ip_address
fi
ssh -i $FIRESIM_PEM_FILE -o "StrictHostKeyChecking no" -t $IP_ADDR 'bash -l -s' < $1 "$2"
}

View File

@ -0,0 +1,5 @@
#! /usr/bin/env expect
set timeout -1
spawn firesim managerinit
send -- "\r"
expect eof

View File

@ -0,0 +1,44 @@
#!/bin/bash
set -ex
set -o pipefail
if [ $# -ne 1 ]; then
echo "$0 <FULL HASH TO TEST>"
exit 1
fi
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/defaults.sh
FULL_HASH=$1
# get the userdata file to launch manager with
rm -rf machine-launch-script.sh
wget https://raw.githubusercontent.com/firesim/firesim/$FULL_HASH/scripts/machine-launch-script.sh
# launch manager
$SCRIPT_DIR/../../deploy/awstools/awstools.py \
launch \
--inst_type c5.4xlarge \
--user_data_file $PWD/machine-launch-script.sh \
2>&1 | tee $IP_ADDR_FILE
rm -rf machine-launch-script.sh
# make sure managerinit finishes properly
run "timeout 10m grep -q \".*machine launch script complete.*\" <(tail -f machine-launchstatus)"
# setup the repo (similar to ci)
run "git clone https://github.com/firesim/firesim.git"
run "cd firesim/ && git checkout $FULL_HASH"
run "cd firesim/ && ./build-setup.sh --fast"
run "cd firesim/sw/firesim-software && ./init-submodules.sh"
# use local aws permissions (for now bypass the manager)
copy ~/.aws/ $IP_ADDR:~/.aws
copy ~/firesim.pem $IP_ADDR:~/firesim.pem
copy firesim-managerinit.expect $IP_ADDR:~/firesim-managerinit.expect
run "cd firesim && source sourceme-f1-manager.sh && cd ../ && ./firesim-managerinit.expect"
echo "Success"

View File

@ -0,0 +1,15 @@
#!/bin/bash
set -ex
set -o pipefail
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/defaults.sh
cd .. # firesim
run "cd firesim/ && source sourceme-f1-manager.sh && sw/firesim-software && ./marshal -v build br-base.json && ./marshal -v install br-base.json"
run "cd firesim/ && source sourceme-f1-manager.sh && cd deploy/workloads/ && make allpaper"
run "cd firesim/ && source sourceme-f1-manager.sh && cd deploy/workloads/ && ./run-all.sh"
echo "Success"

2
scripts/aws-setup.py Normal file → Executable file
View File

@ -1,3 +1,5 @@
#/usr/bin/env python3
""" This script configures your AWS account to run FireSim. """
import boto3

View File

@ -4,6 +4,8 @@ set -ex
set -o pipefail
echo "machine launch script started" > /home/centos/machine-launchstatus
sudo chgrp centos /home/centos/machine-launchstatus
sudo chown centos /home/centos/machine-launchstatus
{
sudo yum install -y ca-certificates
@ -48,36 +50,28 @@ sudo yum -y install graphviz python-devel
# used for CI
sudo yum -y install expect
# pip2 no longer installed on FPGA developer AMIs
sudo yum -y install python-pip
# In the event it is (as on an older AMI), upgrade it just in case
sudo pip2 install --upgrade pip==20.3.4
# these need to match what's in deploy/requirements.txt
sudo pip2 install fabric==1.14.0
sudo pip2 install boto3==1.6.2
sudo pip2 install colorama==0.3.7
sudo pip2 install argcomplete==1.9.3
sudo pip2 install graphviz==0.8.3
# upgrade pip
sudo pip3 install --upgrade pip==21.3.1
# install requirements
sudo python3 -m pip install fab-classic==1.19.1
sudo python3 -m pip install boto3==1.20.21
sudo python3 -m pip install colorama==0.4.3
sudo python3 -m pip install argcomplete==1.12.3
sudo python3 -m pip install graphviz==0.19
# for some of our workload plotting scripts
sudo pip2 install --upgrade --ignore-installed pyparsing
sudo pip2 install numpy==1.16.6
sudo pip2 install kiwisolver==1.1.0
sudo pip2 install matplotlib==2.2.2
sudo pip2 install pandas==0.22.0
# new awscli on 1.6.0 AMI is broken with our versions of boto3
sudo pip2 install awscli==1.15.76
# pip2 should install pytest 4.6.X as it's the last py2 release. see:
# https://pytest.org/en/latest/py27-py34-deprecation.html#what-this-means-for-general-users
sudo pip2 install pytest
# moto 1.3.1 is newest version that will work with boto3 1.6.2
sudo pip2 install moto==1.3.1
sudo python3 -m pip install pyparsing==3.0.6
sudo python3 -m pip install numpy==1.19.5
sudo python3 -m pip install kiwisolver==1.3.1
sudo python3 -m pip install matplotlib==3.3.4
sudo python3 -m pip install pandas==1.1.5
sudo python3 -m pip install awscli==1.22.21
sudo python3 -m pip install pytest==6.2.5
sudo python3 -m pip install moto==2.2.17
# needed for the awstools cmdline parsing
sudo pip2 install pyyaml
sudo python3 -m pip install pyyaml==5.4.1
sudo activate-global-python-argcomplete
# Upgrading pip2 clobbers the pip3 installation paths.
sudo yum reinstall -y python36-pip
# setup argcomplete
activate-global-python-argcomplete
} 2>&1 | tee /home/centos/machine-launchstatus.log

View File

@ -2,10 +2,21 @@
package firesim
import java.io.File
import scala.io.Source
import scala.sys.process.{stringSeqToProcess, ProcessLogger}
/**
* NB: not thread-safe
* An base class for implementing FireSim integration tests that call out to the Make
* buildsystem. These tests typically have three steps whose results are tracked by scalatest:
* 1) Elaborate the target and compile it through golden gate (ElaborateAndCompile)
* 2) Compile a metasimulator for the generated RTL (compileMlSimulator)
* 3) Run the metasimulator. Running a metasimualtion is somewaht
* target-specific and is handled differently by different subclasses.
*
* Some tests inspect the simulation outputs, or run other simulators. See the
* [[TutorialSuite]] for examples of that.
*
* NB: Not thread-safe.
*/
abstract class TestSuiteCommon extends org.scalatest.flatspec.AnyFlatSpec {
@ -98,9 +109,48 @@ abstract class TestSuiteCommon extends org.scalatest.flatspec.AnyFlatSpec {
}
}
}
/**
* Extracts all lines in a file that begin with a specific prefix, removing
* extra whitespace between the prefix and the remainder of the line
*
* @param filename Input file
* @param prefix The per-line prefix to filter with
* @param linesToDrop Some number of matched lines to be removed
* @param headerLines An initial number of lines to drop before filtering.
* Assertions, Printf output have a single line header.
* MLsim stdout has some unused output, so set this to 1 by default
*
*/
def extractLines(filename: File, prefix: String, linesToDrop: Int = 0, headerLines: Int = 1): Seq[String] = {
val lines = Source.fromFile(filename).getLines.toList.drop(headerLines)
lines.filter(_.startsWith(prefix))
.dropRight(linesToDrop)
.map(_.stripPrefix(prefix).replaceAll(" +", " "))
}
/**
* Diffs two sets of lines. Wrap calls to this function in a scalatest
* behavior spec. @param aName and @param bName can be used to provide more
* insightful assertion messages in scalatest reporting.
*/
def diffLines(
aLines: Seq[String],
bLines: Seq[String],
aName: String = "Actual output",
bName: String = "Expected output"): Unit = {
assert(aLines.size == bLines.size && aLines.nonEmpty,
s"\n${aName} length (${aLines.size}) and ${bName} length (${bLines.size}) differ.")
for ((a, b) <- bLines.zip(aLines)) {
assert(a == b)
}
}
}
// HACK: Hijacks TestSuiteCommon to run the MIDAS unit tests
/**
* Hijacks TestSuiteCommon (mostly for make related features) to run the synthesizable unit tests.
*/
abstract class MidasUnitTestSuite(unitTestConfig: String, shouldFail: Boolean = false) extends TestSuiteCommon {
val targetTuple = unitTestConfig
// GENERATED_DIR & OUTPUT_DIR are only used to properly invoke `make clean`

View File

@ -62,8 +62,17 @@ FASEDMemoryTimingModel::FASEDMemoryTimingModel(
size_t delimit_idx = sub_arg.find("=");
size_t suffix_idx = sub_arg.find(suffix);
std::string key = sub_arg.substr(0, suffix_idx).c_str();
int value = std::stoi(sub_arg.substr(delimit_idx+1).c_str());
model_configuration[key] = value;
// This is the only nullary plusarg supported by fased
// All other plusargs are key-value pairs that will be written to the bridge module
if (key == std::string("useHardwareDefaultRuntimeSettings")) {
require_all_runtime_settings = false;
} else if (suffix_idx == std::string::npos) {
throw std::runtime_error("[FASED] unknown nullary plusarg: " + key);
} else {
int value = std::stoi(sub_arg.substr(delimit_idx+1).c_str());
model_configuration[key] = value;
}
}
}
@ -125,11 +134,16 @@ void FASEDMemoryTimingModel::init() {
}
if (!exclude) {
char buf[100];
sprintf(buf, "No value provided for configuration register: %s", pair.first.c_str());
throw std::runtime_error(buf);
if (require_all_runtime_settings) {
char buf[100];
sprintf(buf, "[FASED] No value provided for configuration register: %s", pair.first.c_str());
throw std::runtime_error(buf);
} else {
auto init_val = read(pair.first);
fprintf(stderr, "[FASED] Using hardware default of %u for configuration register %s\n", init_val, pair.first.c_str());
}
} else {
fprintf(stderr, "Ignoring writeable register: %s\n", pair.first.c_str());
fprintf(stderr, "[FASED] Ignoring writeable register: %s\n", pair.first.c_str());
}
}
}

View File

@ -122,6 +122,13 @@ private:
bool has_latency_histograms() { return histograms.size() > 0; };
size_t mem_size;
// By default, FASED requires that plus args for all timing model parameters
// are passed in to prevent accidental misconfigurations (ex. when
// DRAM timing parameters are passed to an LBP). When this is set, using the plus arg
// +mm_useHardwareDefaultRuntimeSettings_<idx>,
// the driver will instead use the hardware reset values (which map to the values emitted in the
// runtime.conf) and print those values to the log instead.
bool require_all_runtime_settings = true;
};
#endif // __FASED_MEMORY_TIMING_MODEL_H

View File

@ -1,4 +1,5 @@
#!/usr/bin/python
#!/usr/bin/env python3
import optparse
import subprocess
import json

View File

@ -168,12 +168,12 @@ class LLCModel(cfg: BaseConfig)(implicit p: Parameters) extends NastiModule()(p)
val mshr_available = mshrs.exists({m: MSHR => m.available() })
val mshr_next_idx = mshrs.indexWhere({ m: MSHR => m.available() })
// TODO: Put this on a switch
val mshrs_allocated = mshrs.count({m: MSHR => m.valid})
assert((mshrs_allocated < io.settings.activeMSHRs) || !mshr_available,
assert((mshrs_allocated < RegNext(io.settings.activeMSHRs)) || !mshr_available,
"Too many runtime MSHRs exposed given runtime programmable limit")
assert((mshrs_allocated === io.settings.activeMSHRs) || mshr_available,
"Too few runtime MSHRs exposed given runtime programmable limit")
assert((mshrs_allocated === RegNext(io.settings.activeMSHRs)) || mshr_available,
"Too few runtime MSHRs exposed given runtime programmable limit.")
val s2_ar_mem = Module(new Queue(new NastiReadAddressChannel, 2))
val s2_aw_mem = Module(new Queue(new NastiWriteAddressChannel, 2))

View File

@ -67,6 +67,7 @@ class LatencyPipe(cfg: LatencyPipeConfig)(implicit p: Parameters) extends SplitT
wResp.bits := writePipe.io.deq.bits.xaction
writePipe.io.deq.ready := wResp.ready && writeDone
assert(writePipe.io.enq.ready || !newWReq, "LBP write latency pipe would overflow.")
// ***** Read Latency Pipe *****
val readPipe = Module(new Queue(new ReadPipeEntry, cfg.maxReads, flow = true))
@ -79,5 +80,7 @@ class LatencyPipe(cfg: LatencyPipeConfig)(implicit p: Parameters) extends SplitT
rResp.valid := readPipe.io.deq.valid && readDone
rResp.bits := readPipe.io.deq.bits.xaction
readPipe.io.deq.ready := rResp.ready && readDone
assert(readPipe.io.enq.ready || !nastiReq.ar.fire, "LBP read latency pipe would overflow.")
}

View File

@ -241,4 +241,6 @@ abstract class SplitTransactionModel(cfg: BaseConfig)(implicit p: Parameters)
awQueue.io.enq.bits := nastiReq.aw.bits
awQueue.io.enq.valid := nastiReq.aw.fire
awQueue.io.deq.ready := newWReq
assert(awQueue.io.enq.ready || !nastiReq.aw.fire,
"AW queue in SplitTransaction timing model would overflow.")
}

View File

@ -80,22 +80,35 @@ abstract class WidgetImp(wrapper: Widget) extends LazyModuleImp(wrapper) {
// For inputs, generates a registers and binds that to the map
// For outputs, direct binds the wire to the map
def attachIO(io: Record, prefix: String = ""): Unit = {
def innerAttachIO(node: Data, name: String): Unit = node match {
/**
* For FASED memory timing models, initalize programmable registers to defaults if provided.
* See [[midas.models.HasProgrammableRegisters]] for more detail.
*/
def getInitValue(field: Bits, parent: Data): Option[UInt] = parent match {
case p: midas.models.HasProgrammableRegisters if p.regMap.isDefinedAt(field) =>
Some(p.regMap(field).default.U)
case _ => None
}
def innerAttachIO(node: Data, parent: Data, name: String): Unit = node match {
case (b: Bits) => (DataMirror.directionOf(b): @unchecked) match {
case ActualDirection.Output => attach(b, s"${name}", ReadOnly)
case ActualDirection.Input => genWOReg(b, name)
case ActualDirection.Input =>
genAndAttachReg(b, name, getInitValue(b, parent))
}
case (v: Vec[_]) => {
(v.zipWithIndex).foreach({ case (elm, idx) => innerAttachIO(elm, s"${name}_$idx")})
(v.zipWithIndex).foreach({ case (elm, idx) => innerAttachIO(elm, node, s"${name}_$idx")})
}
case (r: Record) => {
r.elements.foreach({ case (subName, elm) => innerAttachIO(elm, s"${name}_${subName}")})
r.elements.foreach({ case (subName, elm) => innerAttachIO(elm, node, s"${name}_${subName}")})
}
case _ => new RuntimeException("Cannot bind to this sort of node...")
}
io.elements.foreach({ case (name, elm) => innerAttachIO(elm, s"${prefix}${name}")})
io.elements.foreach({ case (name, elm) => innerAttachIO(elm, io, s"${prefix}${name}")})
}
def attachDecoupledSink(channel: DecoupledIO[UInt], name: String): Int = {
crRegistry.allocate(DecoupledSinkEntry(channel, name))
}

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import os.path
import argparse

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import os.path
import argparse

Some files were not shown because too many files have changed in this diff Show More